「石を贈らない」またはゲーム「呪われた土地」のリソースの配置方法







多くのロシアのゲームを覚えていますか? 定性的? 忘れられない? はい、そうでした。 あなたが35歳以上であるか、ロシアのゲーム業界のファンなら、おそらく呪われた土地に精通しているでしょう。







物語は非常に普遍的に始まりました:夏、暑さ。 特別なことは何もありません。ラップトップのハードドライブのコンテンツをゆっくりと閲覧するとき、私の視線は数年の間アイドル状態だったおなじみのドラゴンのアイコンが付いたフォルダーをつかみました。







ゲームのどのファンが中身を知りたがらないのでしょうか?







はじめに



ゲーム情報



Cursed Lands-または、CISの外で呼ばれたように、 Evil Islands:Curse of the Lost Soul 、2000年にリリースされたステルスRPGゲーム。 このゲームはNival Interactiveによって開発されました。NivalInteractiveは、当時すでに一連のAllodaゲーム(海外のRage of Mages)として確立されていました。 モスクワ州立大学のほとんどの卒業生はそこで働いていました-彼らは完全に3次元の世界を持つ最初のゲームの1つを実現することができました。

2010年、Mail.Ru( information )がタイトルを譲渡しましたが、ゲームはNivalに代わってGOGストアで販売されています。







比較的最近、ゲームは18歳になりました-CISでのリリース日である10月26日の誕生日と見なされます。 時代にもかかわらず、公式のマスターサーバーはまだ使用中です。定期的に、誰かがGipatの森をい回り、仲間のチームで12個または2個のスケルトンを攻撃することにします。







記事について簡単に



当初、私の目標は、標準ライブラリのみを使用して、Python 3で「自分用」の一方向コンバータを作成することだけでした。 しかし、その過程で、フォーマットに関する文書化がスムーズに始まり、何らかの形で出力を標準化しようとしました。 一部の形式では、構造はKaitai Structを使用して記述されました。 その結果、すべてがこの記事とwikiを形式で書くことになりました。







すぐに注意します。ほとんどの場合、ゲームファイルは既に調査されており、ファンエディターが作成されています。 ただし、情報は非常に断片化されており、パブリックドメインの形式の完全な説明はほとんどありません。また、変更を作成するための適切なセットもありません。







...そしてそれを読む方法



スキーム(.ksyファイル)はすべての形式で提供され、2回クリックするだけでいくつかの最も一般的な言語のコードに変換できます。







残念ながら、すでにこの記事を書いている最後の段階で、尊敬されるHabrはYAML(およびJSON)を強調表示する方法を知らず、すべてのスキームがそれを使用していることがわかりました。 これは大きな問題ではありませんが、スキームを読むのが不便な場合は、NPPなどのサードパーティのエディターにコピーすることをお勧めします。







リソースとその場所



このゲームは、ライブラリ、ランチャー、および実際にはリソースが詰め込まれたエンジンを含むポータブルアプリケーションです。







これは興味深いです 。ゲームの設定は、ほぼ完全にレジストリに保存されます。 GOGバージョンのカメラのバグは、インストーラーが正しいデフォルト値を登録しないという事実によるものです。

ゲームフォルダの内容を一見すると、ASIとREGという2つの新しいファイル拡張子がすぐにわかります。

1つ目は動的ライブラリであり、これは考慮しません(これはリバースエンジニアリングの専門家によって行われます)が、2つ目はゲームの最初のネイティブファイル形式です。







REG



このタイプのファイルは、既知のテキストINIファイルのバイナリシリアル化です。

コンテンツは、キーとその値を格納するセクションに分割されます。 REGファイルはこの階層を保持しますが、データの読み取りと解析を高速化します-2000年には、これは明らかに重要でした。







一般に、この図の構造を説明できます。







構造説明
meta: 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
      
      





これは興味深いものです 。2002年、Nivalはゲームのコミュニティといくつかのツールを共有しました( サイトスナップショット )-それらの1つはREGのINIシリアライザーでした。 ご想像のとおり、デシリアライザーは公式ではありませんが、ほとんどすぐに登場しました。

開始フォルダが整理されたら、サブディレクトリに進みましょう。

最初の外観は、CAMファイルを含むカメラフォルダーにあります。







カム



非常に単純な形式は、時間の経過とともにカメラの位置を単純にパックすることです。 カメラは位置と回転によって記述されます。 他の2つのフィールドは、おそらく移動シーケンスの時間とステップです。













構造説明
 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
      
      





次のフォルダー-Res、(予想外!)アーカイブであるRESファイルが保存されます。







RES



この形式は他の拡張機能の下に隠されている場合がありますが、元の形式はまだRESです。

データ構造は、ファイルへのランダムアクセスを持つアーカイブの非常に典型的なものです。内部のファイルに関する情報を保存するためのテーブル、名前のテーブル、ファイルの内容があります。

ディレクトリ構造は名前に直接含まれています。







2つの非常に興味深い事実に注目する価値があります。







  1. アーカイブは、ファイル情報をクローズドハッシュのリンクリストにロードするために最適化されています。
  2. ファイルの内容は一度保存できますが、別の名前で参照します。 私の知る限り、この事実はファンのリパックで使用されていました。これにより、ゲームのサイズが大幅に縮小されました。 元のディストリビューションでは、アーカイブの最適化は使用されませんでした。








構造説明
 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
      
      





これは興味深いものです。ロシア語版のゲームでは、Speech.resアーカイブに2つのサブディレクトリsとtが含まれ、内容がまったく同じです。そのため、アーカイブサイズが2倍になります。

これで、すべてのアーカイブを解凍できます(ネスト可能):









アーカイブ内のファイルに拡張子がない場合は、BONおよびANMアーカイブ用に親拡張子を付けます。







受信したすべてのファイルを4つのグループに分割することもできます。







  1. テクスチャー
  2. データベース
  3. モデル
  4. レベルファイル。


シンプルから始めましょう-テクスチャを使って。







MMP



実際には、テクスチャ。 画像パラメータ、MIPレベルの数、使用されている圧縮を示す小さな見出しがあります。 ヘッダーの後に、サイズの降順でMIPイメージレベルがあります。













構造説明
 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
      
      





可能なピクセルパッケージ形式:







fourcc 説明
44 44 00 00 ARGB4
44 58 54 31 Dxt1
44 58 54 33 Dxt3
50 4E 54 33 PNT3-RLE圧縮ARGB8
50 56 00 00 R5G5B5
51 55 00 00 A1R5G5B5
88 88 00 00 ARGB8


PNT3について

画像形式がPNT3の場合、解凍後のピクセル構造はARGB8です。 bits_per_pixel



圧縮画像のサイズ(バイト単位)。







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
      
      





これは興味深いです。テクスチャの一部は垂直に反映されます(または一部は反映されませんか?)。

そして、ゲームは透明性に非常にjeしています-画像にアルファチャンネルがある場合、透明ピクセルの色は正確に黒でなければなりません。 または白-なんて幸運。

単純なフォーマットは終了しました。より厳格なフォーマットに移りましょう-かつて、MODメーカーのランクは次のフォーマット用の独自の編集ツールを猛烈に保持しましたが、無駄ではありませんでした。 私はあなたに警告しました。







データベース(* DBなど)



この形式は説明するのが非常に不便です-基本的に、これはノード(またはレコードテーブル)のシリアル化されたツリーです。 ファイルは、指定されたフィールドタイプを持つ複数のテーブルで構成されます。 一般構造:テーブルは共通の「ルート」ノードにネストされ、レコードはテーブル内のノードです。







各ノードで、そのタイプとサイズが指定されます:







 unsigned char type_index; unsigned char raw_size; //      unsigned length; //     read(raw_size); if (raw_size & 1) { length = raw_size >> 1; for (int i = 0; i < 3; i++) length <<= 8; read(raw_size); length += raw_size; } else length = raw_size >> 1;
      
      





テーブルフィールドのタイプは、テーブルのフォーマット文字列からインデックスによって取得され、実際のタイプは取得された値によって決定されます。







フィールドタイプ
指定 説明
S ひも
私は 4b int
うん 4b符号なし
F 4bフロート
X ビットバイト
f 浮動小数点配列
私は int配列
B ブール
b ブール配列
H 不明な16進バイト
T 時間
0 記載なし
1 0FII



2 SUFF



3 FFFF



4 0SISS



5 0SISS00000U





拠点の説明

アイテム(.idb)



テーブル 構造
素材 SSSIFFFIFIFfIX



武器 SSISIIIFFFFIFIXB00000IHFFFfHHFF



アーマー SSISIIIFFFFIFIXB00000ffBiHH



クイックアイテム SSISIIIFFFFIFIXB00000IIFFSbH



クエストアイテム SSISIIIFFFFIFIXB00000Is



アイテムの販売 SSISIIIFFFFIFIXB00000IHI





スイッチ(.ldb)



テーブル 構造
スイッチのプロトタイプ SfIFTSSS





スキルとスキル(.pdb)



テーブル 構造
能力 SSI0000000s



スキル SSI0000000SSIIIFFFIIIIBI





フットプリント(prints.db)



テーブル 構造
血痕 0S11



炎の痕跡 0S110000001



足跡 0S11





スペル(.sdb)



テーブル 構造
プロトタイプ SSSFIFIFFFFIIIIUSSIIbIXFFFFF



修飾子 SSFIFFISX



パターン 0SssSX



アーマーテンプレート 0SssSX



武器のパターン 0SssSX





クリーチャー(.udb)



テーブル 構造
破損部品 SffUU



人種 SUFFUUFfFUUf222222000000000000SssFSsfUUfUUIUSBFUUUU



モンスターのプロトタイプ SSIUIFFFSFFFFFFFFFUFFFFFFff33sfssSFFFFFUFUSF



NPC SUFFFFbbssssFUB





叫び声(acks.db)



テーブル 構造
答え 0S0000000044444444444444444444445444444444444



悲鳴 0S0000000044444



その他 0S0000000044





クエスト(.qdb)



テーブル 構造
タスク SFIISIIs



ブリーフィング SFFsSsssssI





これは興味深いです。 2002年1月16日、Nivalはcsv形式のマルチプレイヤーのソースベースと、ゲーム形式のユーティリティコンバーター( サイトスナップショット )を投稿しました。 当然、逆コンバーターの表示は遅くありませんでした。 また、modmakerからフィールドとそのタイプを説明するドキュメントが少なくとも2つありますが、それらを読むことは非常に困難です。


Adb



特定のタイプのユニットのアニメーションデータベース。 上記の* DBとは対照的に、これは非常に「人間」です。これは、静的フィールドサイズの単一レベルのテーブルです。













構造説明
 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'
      
      





これは興味深いものです。いくつかのユニットでは、部分的に切り捨てられたデータベース形式が使用されますが、ほとんど検討されていません。

データベースを扱ったので、商業的な休憩を宣言します。 しかし、我々は何も宣伝しません-私たちの方法ではありません。 次に便利なもの、クリーチャーファイルの名前の付け方をより適切に示します。







モデル名の形式



名前は2文字のグループ-論理「レベル」の略語から収集されます。

たとえば、女性キャラクターはunhufe



- Unit > Human > Female



であり、 initwesp



- Inventory > Item > Weapon > Spear



、つまり在庫の槍(背中ではなく、それは良い)です。







名前要素の完全なツリー:
 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
      
      





これは興味深いです。この分類によれば、キノコは木であり、ゴブリンを持つゴーレムは兄弟であり、Tka-Rickはモンスターです。 また、ここでは、怪物の「働く」名前を見ることができます。これは、D&Dの名前と疑わしく似ています-見る人(邪眼)、サキュブ(ハーピー)、鬼(人食い人種)、ドリアド(森の人)。

道徳的に休んで、私たちは真っ向からモデルに飛び込みます。 それらは、互いにリンクされたいくつかの形式で表示されます。







Lnk



論理的に-モデルの基礎。 モデルのパーツの階層を、現代の3Dモデリングの観点から説明します-ボーンの階層。













構造説明
 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
      
      





親ボーンの親名は空の文字列(長さ0)です。







骨はありますが、名前を付けて組み立てるだけでは十分ではありません。骨に組み立てる必要があります。







ボン



前述のように、この形式(アーカイブでない場合)は、親パーツに対するモデルのパーツ(ボーン)の位置を設定します。 回転せずにオフセットのみが保存されます-最新のフォーマットとの違いの1つです。













構造説明
 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
      
      





ご覧のとおり、1つのオフセットには数が多すぎます-実際、ここで最初にゲームエンジンの重要な機能の1つであるトリリニアモデル補間に出会いました。







仕組み:モデルには、条件、強度、器用さ、成長という3つの補間パラメーターがあります。 モデルには8つの極端な状態もあります。 パラメータを使用して、トライリニア補間により最終モデルを取得できます。







アルゴリズム自体
 def trilinear(val, coefs=[0, 0, 0]): # Linear interpolation by str t1 = val[0] + (val[1] - val[0]) * coefs[1] t2 = val[2] + (val[3] - val[2]) * coefs[1] # Bilinear interpolation by dex v1 = t1 + (t2 - t1) * coefs[0] # Linear interpolation by str t1 = val[4] + (val[5] - val[4]) * coefs[1] t2 = val[6] + (val[7] - val[6]) * coefs[1] # Bilinear interpolation by dex v2 = t1 + (t2 - t1) * coefs[0] # Trilinear interpolation by height return v1 + (v2 - v1) * coefs[2]
      
      





これは興味深いです。たとえば、石のドアやチェストを開くなど、一部のオブジェクトをアニメーション化するために、トライリニアモデルの補間が使用されます。

今こそ、モデル自体の一部を見るときです。









おそらく、この集会は理解することが不可能です。 彼の説明とブレンダーのプラグインはネット上で見つけることができますが、それらを知っていてもすぐには気づきません。 ご覧ください:













構造説明
 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
      
      





難しさは何ですか? したがって、法線と頂点のデータは4つのブロックに格納され、頂点も補間のために8つのブロックに配置されます。







これは興味深いです。おそらく、1999年以降Intelプロセッサに登場したSSE命令の助けを借りて処理を高速化するために、このようなグループ化が行われました。

さて、私たちはモデルを読んで構成しましたが、何かが欠けています。 まさに-アニメーション!







あん



アニメーションは、キーの状態としてコンポーネント形式で保存されます。 興味深い事実は、骨格アニメーションだけでなく頂点モーフィングもサポートされていることです。













構造説明
 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
      
      





それだけです-これで本格的なモデルができました。新しくレンダリングされたヤドカリを賞賛できます:













懐かしい瞬間

トカゲに必要なものを見つける



彼の家でトカゲとの会話







隠者リザード:あなたは来た、男。 これはいいです。







ザック:あなたが私に伝えたかったことはそれだけですか?







隠者リザード:あなたは再び急いでいます。 私はあなたの質問を覚えており、それらに答えます。 取引をするために鉄の人々に来ました。 しかし、私は彼らがあなたにどうしたかを見ました。 彼らは言葉を持ちません、私は彼らを信じることを止めました。 あなたは言葉を守りました。 取引があなたに提供されます。







隠者リザード:人々は金が大好きです。 金のトカゲは面白くない。 あなたは私の仕事を完了します、そして私はあなたに私が持っている金を与えます。 たくさんの金があります。







ザック(思慮深く、あまり興味がない)うーん...ゴールド...それは確かに傷つけないだろう...







ザック:私が長年探していた老魔術師の居場所を見つけてくれるといいのですが。 結局のところ、トカゲは古代の人々であり、あなたはそれを知ることができます!







隠者リザード:そのとおりです。 トカゲは古代の人々です。 私たちは老人について知っているすべてを集めることができます。 私のミッションを完了することに同意しますか?







ザック:なんて話だ! すべてがすでに行われていることを考慮してください。







Hermit Lizard (まじめに)もうやったの? 私をだましますか?







ザック:実際、私は冗談を言いたかった。そうでなければ、あなたは本当に真剣だった。







隠者トカゲ:なるほど。 これは冗談です。 私も冗談を言うことができると思います。 それから。 そして今、運河に水を戻す必要があります。 オークは私たちから水を盗みました。







隠者トカゲ:水に沿って南に行きます。 ダムと運河が見えます。 ダムを上げる必要があります。 レバレッジ。 あげます チャネルをブロックする必要があります。 石。 石をあげません 彼はすでに運河の端に横たわっています。 ダムの上流。 石は重いです。 オークが掘ったとき、彼らは長い間彼を持ち上げました。 あなたが彼を押すと、彼はすぐに後退します。







隠者リザード:その後、戻ってきてください。 昔の魔術師について学んだことをすべてお話しします。







ザック:手に! しかし、ちなみに、ストーリーに少しコインを追加しても、私はまったく気分を害することはありません。







隠者のトカゲ:さらに南の浅瀬に住んでいる私の親relativeにコインを求めてください。 3番目に並んでいる最も遠い砂浜の島に行きます。 宝物はあなたのものになります!







トカゲの隠者(彼自身へ)奇妙な。 この男はユーモアが大好きです。 私は冗談を言っていました。 男は笑いませんでした。 とても奇妙です。







さて、最も興味深いのは、マップの保存方法です。







MP



これはマップヘッダーファイルです。 不幸な偶然の一致により、拡張子はマルチプレイヤーの保存ファイルの拡張子と一致しますが、これは考慮しません。







最初に、風景の一般的な説明をする必要があります。









さらに、マップマテリアルの説明と、アニメーションタイル(水や溶岩など)があります。













構造説明
 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
      
      





terrain type 種類
0
1
2
3


material 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


, 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;
      
      





3d



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
Float 4 4
LeverStats 12
Null 0
Plot 12 3 floats (vec3)
Plot2DArray
Quaternion 16 4 floats (vec4)
Record >8
Rectangle
ひも
StringArray >4
StringEncrypted >4
UnitStats 180
Unknown


type_id
type_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 ひも VSS_TITLE
0x00001E09 ひも VSS_COMMANDS
0x00001E0A Byte VSS_ISSTART
0x00001E0B Record VSS_LINK
0x00001E0C ひも VSS_GROUP
0x00001E0D Byte VSS_IS_USE_GROUP
0x00001E0E Record VSS_VARIABLE
0x00001E0F StringArray VSS_BS_CHECK
0x00001E10 StringArray VSS_BS_COMMANDS
0x00001E11 ひも VSS_CUSTOM_SRIPT
0x0000A000 Record OBJECTDBFILE
0x0000AA00 Null LIGHT_SECTION
0x0000AA01 Record 軽い
0x0000AA02 Float LIGHT_RANGE
0x0000AA03 ひも LIGHT_NAME
0x0000AA04 Plot LIGHT_POSITION
0x0000AA05 Dword LIGHT_ID
0x0000AA06 Byte LIGHT_SHADOW
0x0000AA07 Plot LIGHT_COLOR
0x0000AA08 ひも LIGHT_COMMENTS
0x0000ABD0 Record WORLD_SET
0x0000ABD1 Plot WS_WIND_DIR
0x0000ABD2 Float WS_WIND_STR
0x0000ABD3 Float WS_TIME
0x0000ABD4 Float WS_AMBIENT
0x0000ABD5 Float WS_SUN_LIGHT
0x0000B000 Record OBJECTSECTION
0x0000B001 Record OBJECT
0x0000B002 Dword NID
0x0000B003 Dword OBJTYPE
0x0000B004 ひも OBJNAME
0x0000B005 Null OBJINDEX
0x0000B006 ひも OBJTEMPLATE
0x0000B007 ひも OBJPRIMTXTR
0x0000B008 ひも OBJSECTXTR
0x0000B009 Plot OBJPOSITION
0x0000B00A Quaternion OBJROTATION
0x0000B00B Null OBJTEXTURE
0x0000B00C Plot OBJCOMPLECTION
0x0000B00D StringArray OBJBODYPARTS
0x0000B00E ひも PARENTTEMPLATE
0x0000B00F ひも 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 ひも 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 ひも SOUND_NAME
0x0000CC06 Dword SOUND_MIN
0x0000CC07 Dword SOUND_MAX
0x0000CC08 ひも 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 ひも PARTICL_COMMENTS
0x0000DD05 ひも PARTICL_NAME
0x0000DD06 Dword PARTICL_TYPE
0x0000DD07 Float PARTICL_SCALE
0x0000E000 Record DIRICTORY
0x0000E001 Record FOLDER
0x0000E002 ひも 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 ひも SS_TEXT_OLD
0xACCEECCB StringEncrypted SS_TEXT
0xBBAB0000 Record MAGIC_TRAP
0xBBAB0001 Dword MT_DIPLOMACY
0xBBAB0002 ひも 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 ひも 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 Float 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 Float UNIT_LOGIC_WAIT
0xBBBC000B Byte UNIT_LOGIC_ALARM_CONDITION
0xBBBC000C Float 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 Float TORCH_STRENGHT
0xBBBF0002 Plot TORCH_PTLINK
0xBBBF0003 ひも 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 .

, (, "" ).




















All Articles