
Você se lembra de muitos jogos russos? Qualitativo? Memorável? Sim eles eram. Se você tem mais de 35 anos ou é um fã da indústria de jogos russa, provavelmente está familiarizado com as Terras Amaldiçoadas.
A história começou de maneira muito prosaica: verão, calor. Não há nada de especial a fazer e, ao navegar preguiçosamente pelo conteúdo do disco rígido do laptop, meu olhar pegou uma pasta com o familiar ícone de dragão que estava inativo por alguns anos.
Que fã do jogo não estará interessado em saber o que está dentro?
1. Introdução
Terras Amaldiçoadas - ou, como eram chamadas fora da CEI, Ilhas Malignas: Maldição da Alma Perdida , um jogo de RPG furtivo lançado em 2000. O jogo foi desenvolvido pela Nival Interactive, que na época já havia se estabelecido como uma série de jogos Alloda (Rage of Mages no exterior). A maioria dos graduados da Universidade Estatal de Moscou trabalhou nele - eles foram capazes de realizar um dos primeiros jogos com um mundo completamente tridimensional.
Em 2010, o Mail.Ru ( informações ) transferiu o título para o nome, mas o jogo ainda está sendo vendido na loja GOG em nome da Nival.
Recentemente, o jogo completou 18 anos - o aniversário é considerado 26 de outubro, a data de lançamento na CEI. Apesar da idade, o servidor mestre oficial ainda está em serviço: periodicamente, alguém decide rastejar pelas florestas de Gipat e atingir uma dúzia ou dois esqueletos com um esquadrão de camaradas.
Brevemente sobre o artigo
Inicialmente, meu objetivo era apenas escrever um conversor unidirecional "para mim" no Python 3, usando bibliotecas exclusivamente padrão. No entanto, no processo, a documentação dos formatos começou sem problemas, tentativas de alguma forma padronizar a saída. Para alguns formatos, a estrutura foi descrita usando o Kaitai Struct . Como resultado, tudo resultou na redação deste artigo e no wiki em formatos.
Percebo imediatamente: na maioria das vezes, os arquivos do jogo já foram pesquisados, os editores de fãs foram escritos para eles. No entanto, as informações são extremamente fragmentadas e não há descrição mais ou menos completa dos formatos no domínio público, nem existe um conjunto adequado para a criação de modificações.
... e como lê-lo
Os esquemas (arquivos .ksy) são fornecidos para todos os formatos, que podem ser convertidos em dois cliques no código em vários dos idiomas mais populares.
Infelizmente, já nos últimos estágios de redação deste artigo, descobri que o estimado Habr não é capaz de destacar o YAML (e o JSON), e todos os esquemas o utilizam. Isso não deve ser um grande problema, mas se for inconveniente ler o esquema, posso aconselhá-lo a copiá-lo para um editor de terceiros, por exemplo, NPP.
Recursos e onde eles moram
O jogo é um aplicativo portátil que contém um mecanismo com bibliotecas, um iniciador e, de fato, recursos compactados.
Isso é interessante: as configurações do jogo são quase inteiramente armazenadas no registro. O bug da câmera na versão GOG é devido ao fato de o instalador não registrar os valores padrão corretos.
À primeira vista no conteúdo da pasta do jogo, notamos imediatamente algumas novas extensões de arquivo: ASI e REG.
A primeira é uma biblioteca dinâmica, que não consideraremos (isso é feito por especialistas em engenharia reversa), mas a segunda é o primeiro formato de arquivo nativo do jogo.
REG
Arquivos desse tipo são serialização binária de arquivos INI de texto conhecidos.
O conteúdo é dividido em seções que armazenam chaves e seus valores. Um arquivo REG mantém essa hierarquia, mas acelera a leitura e a análise de dados - em 2000, isso era aparentemente crítico.
Em geral, você pode descrever a estrutura deste diagrama:

Descrição da estruturameta: 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
Isso é interessante: em 2002, o Nival compartilhou algumas ferramentas com a comunidade do jogo ( instantâneo do site ) - uma delas era o serializador INI no REG. Como você pode imaginar, um desserializador apareceu quase imediatamente, embora não seja oficial.
Com a pasta inicial organizada, vamos para os subdiretórios.
A primeira olhada fica na pasta Câmeras que contém os arquivos CAM.
Cam
Um formato muito simples é simplesmente colocar as posições das câmeras ao longo do tempo. A câmera é descrita por posição e rotação. Os outros dois campos são presumivelmente tempo e passo na sequência de movimentos.

Descrição da estrutura 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
Na próxima pasta - Res, (inesperadamente!), Os arquivos RES armazenados são armazenados.
RES
Às vezes, esse formato está oculto em outras extensões, mas o original ainda é exatamente RES.
A estrutura de dados é muito típica para um arquivo morto com acesso aleatório a arquivos: existem tabelas para armazenar informações sobre arquivos, uma tabela de nomes e o conteúdo dos arquivos.
A estrutura de diretórios está contida diretamente nos nomes.
Vale ressaltar dois fatos extremamente interessantes:
- O arquivamento é otimizado para carregar informações do arquivo em uma lista vinculada com hash fechado.
- Você pode armazenar o conteúdo do arquivo uma vez, mas consulte-o com nomes diferentes. Até onde eu sei, esse fato foi usado em uma reembalagem de fãs, onde o tamanho do jogo foi bastante reduzido devido a isso. Na distribuição original, a otimização de arquivo não foi usada.

Descrição da estrutura 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
Isso é interessante: na versão russa do jogo, o arquivo Speech.res contém dois subdiretórios s e t com conteúdo completamente idêntico, e é por isso que o tamanho do arquivo é duas vezes maior - e é por isso que o jogo não cabe em um CD.
Agora você pode descompactar todos os arquivos (pode ser aninhado):
- RES é apenas um arquivo,
- MPR - cenário de níveis de jogo,
- MQ - informações sobre as tarefas do multiplayer,
- ANM - um conjunto de animações,
- MOD - modelo 3d,
- BON - a localização dos ossos do modelo.
Se os arquivos dentro do arquivo morto não tiverem uma extensão, colocaremos a extensão pai - nos arquivos BON e ANM.
Você também pode dividir todos os arquivos recebidos em quatro grupos:
- Texturas
- Bases de dados
- Modelos
- Arquivos de nível.
Vamos começar com o simples - com as texturas.
MMP
Na verdade, a textura. Possui um cabeçalho pequeno, indicando os parâmetros da imagem, o número de níveis de MIP e a compactação usada. Após o cabeçalho, estão os níveis de imagem MIP em ordem decrescente de tamanho.

Descrição da estrutura 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
Possíveis formatos de embalagem de pixel:
fourcc | Descrição do produto |
---|
44 44 00 00 | ARGB4 |
44 58 54 31 | Dxt1 |
44 58 54 33 | Dxt3 |
50 4E 54 33 | PNT3 - ARGB8 comprimido por RLE |
50 56 00 00 | R5G5B5 |
51 55 00 00 | A1R5G5B5 |
88 88 00 00 | ARGB8 |
Sobre o PNT3Se o formato da imagem for PNT3 , a estrutura de pixels após a descompactação é ARGB8; bits_per_pixel
- tamanho da imagem compactada em bytes.
Desembalando o 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
Isso é interessante: algumas das texturas são refletidas verticalmente (ou algumas não são refletidas?).
E o jogo tem muita inveja da transparência - se a imagem estiver em um canal alfa, a cor dos pixels transparentes deve ser exatamente preta. Ou branco - que sorte.
Formatos simples terminaram, vamos para os mais rígidos - ao mesmo tempo, as fileiras dos fabricantes de mods furiosamente mantiveram suas próprias ferramentas de edição para os seguintes formatos, e não em vão. Eu te avisei.
Bancos de dados (* DB e outros)
Esse formato é extremamente inconveniente para descrever - em essência, é uma árvore serializada de nós (ou tabelas de registros). Um arquivo consiste em várias tabelas com os tipos de campo especificados. Estrutura geral: as tabelas são aninhadas em um nó "raiz" comum, os registros são nós dentro de uma tabela.
Em cada nó, seu tipo e tamanho são especificados:
unsigned char type_index; unsigned char raw_size;
O tipo do campo da tabela é obtido pelo índice a partir da string de formato da tabela, o tipo real é determinado pelo valor obtido.
Tipos de campodesignação | a descrição |
---|
S | corda |
Eu | 4b int |
U | 4b não assinado |
F | 4b float |
X | bits byte |
f | matriz flutuante |
eu | matriz int |
B | bool |
b | matriz bool |
H | bytes hexadecimais desconhecidos |
T | tempo |
0 0 | não declarado |
1 | 0FII |
2 | SUFF |
3 | FFFF |
4 | 0SISS |
5 | 0SISS00000U |
Descrição das basesItens (.idb)
mesa | a estrutura |
---|
Materiais | SSSIFFFIFIFfIX |
Arma | SSISIIIFFFFIFIXB00000IHFFFfHHFF |
Armadura | SSISIIIFFFFIFIXB00000ffBiHH |
Itens rápidos | SSISIIIFFFFIFIXB00000IIFFSbH |
Itens de missão | SSISIIIFFFFIFIXB00000Is |
Venda de itens | SSISIIIFFFFIFIXB00000IHI |
Opções (.ldb)
mesa | a estrutura |
---|
Switch prototype | SfIFTSSS |
Habilidades e Habilidades (.pdb)
mesa | a estrutura |
---|
Habilidades | SSI0000000s |
Competências | SSI0000000SSIIIFFFIIIIBI |
Pegadas (impressões.db)
mesa | a estrutura |
---|
Vestígios de sangue | 0S11 |
Traços de chama | 0S110000001 |
Pegadas | 0S11 |
Feitiços (.sdb)
mesa | a estrutura |
---|
Protótipos | SSSFIFIFFFFIIIIUSSIIbIXFFFFF |
Modificadores | SSFIFFISX |
Padrões | 0SssSX |
Modelos de armaduras | 0SssSX |
Padrões de armas | 0SssSX |
Criaturas (.udb)
mesa | a estrutura |
---|
Peças danificadas | SffUU |
Raça | SUFFUUFfFUUf222222000000000000SssFSsfUUfUUIUSBFUUUU |
Protótipos de monstros | SSIUIFFFSFFFFFFFFFUFFFFFFff33sfssSFFFFFUFUSF |
Npc | SUFFFFbbssssFUB |
Gritos (acks.db)
mesa | a estrutura |
---|
As respostas | 0S0000000044444444444444444444445444444444444 |
Gritos | 0S0000000044444 |
Outros | 0S0000000044 |
Missões (.qdb)
mesa | a estrutura |
---|
Missões | SFIISIIs |
Briefings | SFFsSsssssI |
Isso é interessante: 16 de janeiro de 2002 A Nival postou as bases de origem para o multiplayer no formato csv, bem como um conversor de utilidades no formato do jogo ( instantâneo do site ). Naturalmente, o conversor inverso não demorou a aparecer. Também há pelo menos dois documentos descrevendo os campos e seus tipos dos modmakers, mas lê-los é muito difícil.
Adb
Banco de dados de animação para um tipo específico de unidade. Em contraste com o * DB mencionado acima, é bastante "humano" - é uma tabela de nível único com tamanhos de campo estático.

Descrição da estrutura 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'
Isso é interessante: para várias unidades, é usado um formato de banco de dados parcialmente truncado, que dificilmente foi explorado.
Tendo lidado com os bancos de dados, declaramos uma quebra de publicidade. Mas não anunciaremos nada - não nosso método. Melhor indicar o que será útil a seguir - como os arquivos de criaturas são nomeados.
O nome é coletado de grupos de dois caracteres - abreviações do "nível" lógico.
Por exemplo, o personagem feminino não será unhufe
- Unit > Human > Female
e initwesp
- Inventory > Item > Weapon > Spear
, ou seja, uma lança no inventário (não nas costas, e isso é bom).
Árvore completa dos elementos do nome: 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
Isso é interessante: de acordo com essa classificação, cogumelos são árvores, golens com goblins são irmãos e Tka-Rick é um monstro. Também aqui você pode ver os nomes "de trabalho" dos monstros, suspeitosamente semelhantes aos de D&D - observador (olho mau), succub (harpia), ogro (canibal), driad (silvicultores).
Tendo descansado moralmente, mergulhamos de cabeça no modelo. Eles são apresentados em vários formatos vinculados.
Lnk
Logicamente - a base do modelo. Descreve a hierarquia de partes do modelo, em termos da modelagem 3D moderna - a hierarquia dos ossos.

Descrição da estrutura 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
O nome do pai do osso pai é uma sequência vazia (comprimento 0).
Existem ossos, no entanto, não é suficiente nomeá-los e juntá-los - você precisa montá-los em um esqueleto.
Bon
Mencionado anteriormente, esse formato (se não for um arquivo morto) define a posição das partes (ossos) do modelo em relação à parte pai. Somente o deslocamento é armazenado, sem rotação - uma das diferenças dos formatos modernos.

Descrição da estrutura 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 você pode ver, há muitos números para uma mudança - o fato é que aqui encontramos um dos principais recursos do mecanismo de jogo - a interpolação trilinear de modelos.
Como funciona: o modelo possui três parâmetros de interpolação - condicionalmente, força, destreza, crescimento. Existem também 8 estados extremos do modelo. Usando os parâmetros, podemos obter o modelo final por interpolação trilinear.
O próprio algoritmo def trilinear(val, coefs=[0, 0, 0]):
Isso é interessante: a interpolação de modelo trilinear é usada para animar alguns objetos, por exemplo, abrir uma porta de pedra e baús.
Agora é a hora de examinar as partes do modelo em si.
FIG
Talvez este comício seja impossível de entender. Você pode encontrar sua descrição e o plug-in do liquidificador na rede, mas mesmo com eles a conscientização não vem imediatamente. Dê uma olhada:

Descrição da estrutura 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
Qual é a dificuldade? Afinal, os dados de normais e vértices são armazenados em blocos de 4 e os vértices também são organizados em 8 blocos para interpolação.
Isso é interessante: presumivelmente, esse agrupamento foi feito para acelerar o processamento com a ajuda das instruções SSE que apareceram nos processadores Intel desde 1999.
Bem, lemos e compusemos o modelo, mas algo está faltando. Exatamente - animações!
Anm
A animação é armazenada no formato de componente como estados principais. Um fato interessante é que ele suporta não apenas a animação esquelética, mas também a transformação de vértices.

Descrição da estrutura 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
É isso aí - agora temos um modelo completo, você pode admirar o lagarto eremita recém-renderizado:

Um momento de nostalgiaDescubra o que o Lizard precisa
Conversa com o lagarto em sua casa
Lagarto eremita: Você veio, cara. Isso é bom
Zach: Isso é tudo que você queria me dizer?
Hermit Lizard: Você está com pressa novamente. Lembro de suas perguntas e vou respondê-las. Eu vim para as pessoas de ferro para fazer um acordo. Mas eu vi como eles fizeram com você. Eles não têm uma palavra, eu parei de acreditar neles. Você manteve sua palavra. Um acordo será oferecido a você.
Lagarto eremita: As pessoas adoram ouro. Lagartos de ouro são desinteressantes. Você completará minha tarefa e eu lhe darei o ouro que tenho. Há muito ouro.
Zach (pensativo e sem muito interesse) : Hmm ... Ouro ... Certamente não vai doer ...
Zach: Seria melhor se você pudesse me ajudar a descobrir onde o velho mago que eu venho procurando há tanto tempo. Afinal, os lagartos são um povo antigo, e você pode conhecê-lo!
Hermit Lizard: Você está certo. Lagartos são um povo antigo. Eu posso coletar tudo o que sabemos sobre o velho. Você concorda em completar minha missão?
Zach: Que conversa! Considere que tudo já foi feito.
Lagarto eremita (sério) : Já terminou? Você quer me enganar?
Zach: Na verdade, eu queria fazer uma piada, caso contrário você estava falando sério.
O Lagarto Eremita: Entendo. Isso é uma piada. Acho que posso fazer uma piada também. Então E agora preciso que você devolva a água ao canal. Orcs roubaram água de nós.
Hermit Lizard: Vá para o sul ao longo da água. Você verá a barragem e o canal. A barragem deve ser elevada. Alavancagem. Eu darei. O canal precisa estar bloqueado. A pedra Eu não vou dar uma pedra. Ele já está na beira do canal. Rio acima da barragem. A pedra é pesada. Quando os orcs cavaram, eles o ergueram por um longo tempo. Se você empurrá-lo, ele voltará rapidamente.
Hermit Lizard: Depois disso, volte. Vou lhe contar tudo que aprender sobre o velho mago.
Zach: Mão na mão! A propósito, se você adicionar um pouco de moedas à história, não ficarei ofendido.
O lagarto eremita: Para moedas, vá para meus parentes que vivem nas águas rasas mais ao sul. Vá para a ilha mais distante, a terceira consecutiva. Os tesouros serão seus!
Eremita Lagarto (para si mesmo) : Estranho. Este homem adora humor. Eu estava brincando. O homem não riu. Muito estranho
Agora - o mais interessante: como o mapa é armazenado.
MP
Este é o arquivo de cabeçalho do mapa. Devido a uma infeliz coincidência, a extensão coincide com a dos arquivos salvos para vários jogadores, que não consideraremos.
Primeiro, você precisa fornecer uma descrição geral da paisagem:
- o número de "pedaços" - pedaços do cartão 32x32 metros;
- altura máxima (uma vez que a altura dos vértices é armazenada em uma escala inteira);
- número de atlas de ladrilhos.
, — , .

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
tipo de terreno | Tipo |
---|
0 0 | Paisagem base |
1 | Água sem textura |
2 | Grama texturizada |
3 | Água texturizada |
tipo de material | Tipo |
---|
0 0 | grama |
1 | chão |
2 | pedra |
3 | areia |
4 | rock |
5 | campo |
6 | agua |
7 | estrada |
8 | (vazio) |
9 | neve |
10 | gelo |
11 | erva seca |
12 | bolas de neve |
13 | lava |
14 | pântano |
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
— .
Desembalagem normal
10 bits por eixo z, 11 por x e 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 bits por índice no atlas, 8 por número de textura, 2 por rotação
unsigned short texture; unsigned char tile_index = f & 63; unsigned char texture_index = (f >> 6) & 255; unsigned char rotation = (f >> 14) & 3;
3dObtendo paisagem
33 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 |
Flutuar | 4 | 4 |
LeverStats | 12 | |
Null | 0 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 | Flutuar | 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 | Flutuar | WS_WIND_STR |
0x0000ABD3 | Flutuar | WS_TIME |
0x0000ABD4 | Flutuar | WS_AMBIENT |
0x0000ABD5 | Flutuar | 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 | Flutuar | 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 | Flutuar | 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 | Flutuar | UNIT_LOGIC_WAIT |
0xBBBC000B | Byte | UNIT_LOGIC_ALARM_CONDITION |
0xBBBC000C | Flutuar | 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 | Flutuar | 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 — ,