Excel VBAでファイル解析(Directorファイル編)
■Directorファイルの概要
DirectorファイルはAdobe Director(※1)で作成されたファイルで、ファイルの拡張子は「.dir」です。
(※1)Adobe Directorはオーサリングソフトで、タイムライン上に画像や音声を配置してアニメーションを作成可能です(作成したコンテンツを保存したものが、Directorファイルです)。
1980~1990年代にはゲーム・ソフト等に使用されていたようですが、2017年に販売を終了しています。
なお、Wikipediaによると、当初の製品名は「MacroMind Video Works」でしたが、後に「MacroMind Director」→「Macromedia Director」→「Adobe Director」と名称変更されています。
筆者はDirectorという製品を知らなかったんですが、たまたま入手したPippin@(※2)版のソフトがDirectorを使用していたため、Directorファイルの内部構造を調べたという経緯です。
(筆者はPippin@のハードを持っていないため、Excel VBAを駆使して何とか画像だけは見ることができました)
(※2)Pippin@は1990年代にAppleとバンダイが共同開発したゲーム機です。
Directorファイルの仕様を全て理解している訳ではないので、本稿では画像データを取扱うサンプル・プログラムを中心に説明します。
なお、動作確認に使用したDirectorファイルはMAC版のVersion 5で作成されたものです。
⇒Directorファイルの仕様について、公式に開示されている情報はほとんどありませんが、Anthony Kleine(Team Earthquake)のサイト(More Director Movie File Unofficial Documentation)とGitHubで公開されているAbraham Macias Paredesのプログラム(Macromedia Director 5 DRI and DRX files data extractor)が参考になります。
なお、Adobe Directorに固有の概念を予め把握しておいた方が理解し易いと思いますので、「ユーザガイド」の一部を以下に引用しておきます。
- (Director のメタファーの理解)
- Director のユーザインターフェイスのコンセプトは、映画制作のプロセスを模倣しています。
Director で作成する各プロジェクトは、出演俳優(キャスト)、音楽(スコア)、アクションが行われる舞台(ステージ)、および監督(プロジェクトの作者 = ディレクタ)から成り立つ映画と考えることができます。
サウンド、ビデオ、画像、テキスト、ボタンなどのムービーの各メディアエレメントも、映画のキャストのメンバと見なすことができます。 - (中略)
- 実際の映画と同様に、Director の各ムービーにはスコアがあります。
しかし Director ムービーのスコアには音楽以外のものもあります。
Director のスコアウィンドウ内には、各キャストメンバがステージ上にいつ、どの位置に表示されるかについての情報が含まれています。
スコアは、ムービーのアクションを定義するものです。
Directorファイルの構成
Directorファイルの基本構造はRIFF(Resource Interchange File Format)フォーマットになっており、RIFFチャンクの中にDirector固有のサブチャンクが配置されています。
(Directorファイルの基本構造)
識別子 ('RIFX’) |
サイズ (ファイル長 -8) |
フォーム タイプ ('MV93’) |
データ (サブチャンクの集まり) |
Directorファイルのチャンク
Directorファイルに含まれる主なチャンクは下表の通りですが、画像データにアクセスするために必要なチャンクは、括弧書きで簡単な説明を付けた前半の10種類になります。
この10種類のチャンクについて、次項で詳細を説明します。
(主なチャンクの種類)
識別子 (チャンクID) |
説明 |
---|---|
RIFX | Archive Header(またはRIFX Header) (Directorファイル全体) |
imap | Input Map (先頭に配置され、mmapの位置を示すポインター) |
mmap | Memory Map (各チャンクの位置とサイズを示すテーブル) |
KEY* | Key Mapping Pointers (Cast MemberとResourceの対応関係を示すテーブル) |
CAS* | Cast Mapping Pointers (Cast IDとResource IDの対応関係を示すテーブル) |
VWCF | Video Works Configuration (Directorファイルの構成情報を保有) |
VWSC | Video Works Score (コンテンツの詳細情報(Channel Data)を保有) |
CASt | Cast Member Pointers (Cast Memberの位置、Resource IDと基本情報を保有) |
CLUT | Color Lookup Table (カラーパレット) |
BITD | Bitmap Data (ビットマップデータ) |
VWFI | Video Works File Information |
VWFM | VWFM |
Sord | Score Order |
Lctx | Lingo Context |
Lscr | Lingo Script |
THUM | Thumbnail |
Archive Header(またはRIFX Header)のフォーマット
Archive Header(またはRIFX Header)のフォーマットは、下表の通りです。
後続の処理で必要となる情報は保有していません(ファイルの形式をチェックする際に使う位)ので、読み飛ばして大丈夫です。
(Archive Header(またはRIFX Header)のフォーマット)
オフセット | 長さ | 説明 |
---|---|---|
0 | 4 | Chunk ID('RIFX’) |
4 | 4 | Block Size(ファイルサイズ-8) |
8 | 4 | Format Name('MV93’) |
imap(Input Map)のフォーマット
imap(Input Map)のフォーマットは、下表の通りです。
imapはArchive Header(またはRIFX Header)の直後に配置され、mmapの開始位置を示します。
多くのDirectorファイルでは、imapの直後にmmapが配置されていますが、mmapが複数存在するファイルもありますので、必ずimapが指している方のmmapを使用するようにして下さい。
(imap(Input Map)のフォーマット)
オフセット | 長さ | 説明 |
---|---|---|
0 | 4 | Chunk ID('imap’) |
4 | 4 | Data Size(X’00000018′) |
8 | 4 | Memory Map Count(X’00000001′) |
12 | 4 | Memory Map Offset(mmapの開始位置) |
16 | 4 | Memory Map File Version(X’00000000′) |
20 | 2 | Reserved |
22 | 2 | Unknown |
24 | 4 | Reserved |
mmap(Memory Map)のフォーマット
mmap(Memory Map)のフォーマットは、下表の通りです。
mmapは「Properties(固定長)」と「Resources(可変長)」の2つの部分に分かれており、Propertiesの中にResourcesの1エントリーのサイズやエントリー数を保有し、Resourcesの各エントリーが全てのチャンクの開始位置とサイズを保有しています。
個々のチャンクにアクセスする際には、mmapで該当するチャンクの開始位置を取得する必要があります。
下表の中に「Resource ID」という単語が出て来ますが、これはDirectorファイル内のチャンクの連番で、Resourcesのエントリー番号と一致しています(0がRIFX、1がimap、2がmmap、・・・という形になっています)。
(mmap(Memory Map)のフォーマット)
オフセット | 長さ | 説明 |
---|---|---|
Properties | ||
0 | 4 | Chunk ID('mmap’) |
4 | 4 | Data Size |
8 | 2 | Properties Size(X’0018′) (Propertiesのサイズ) |
10 | 2 | Resource Size(X’0014′) (Resources Arrayの1エントリーのサイズ) |
12 | 4 | Max Resource Count (Resources Arrayの最大エントリー数) |
16 | 4 | Used Resource Count (Resources Arrayの使用中のエントリー数) |
20 | 4 | First Junk Resource ID |
24 | 4 | Old Memory Map Resource ID |
28 | 4 | First Free Resource ID |
Resources Array(各エントリのフォーマットは以下の通りです) | ||
0 | 4 | Chunk ID |
4 | 4 | Size |
8 | 4 | Offset |
12 | 2 | Flags |
14 | 2 | Unused |
16 | 4 | Next Resource ID |
KEY*(Key Mapping Pointers)のフォーマット
KEY*(Key Mapping Pointers)のフォーマットは、下表の通りです。
KEY*は「Properties(固定長)」と「Mapping Pointers Array(可変長)」の2つの部分に分かれており、Propertiesの中にMapping Pointers Arrayの1エントリーのサイズやエントリー数を保有し、Mapping Pointers Arrayの各エントリーがCast MemberとResourceの対応関係を保有しています。
Cast Memberが画像の場合は、Mapping Pointers Arrayが2エントリー使用され、1つはBITD、もう1つはTHUMと対応付けられています。
(KEY*(Key Mapping Pointers)のフォーマット)
オフセット | 長さ | 説明 |
---|---|---|
Properties | ||
0 | 4 | Chunk ID('KEY*’) |
4 | 4 | Data Size |
8 | 2 | Properties Size(X’000C’) (Propertiesのサイズ) |
10 | 2 | Key Size(X’000C’) (Mapping Pointers Arrayの1エントリーのサイズ) |
12 | 4 | Max Key Count (Mapping Pointers Arrayの最大エントリー数) |
16 | 4 | Used Key Count (Mapping Pointers Arrayの使用中のエントリー数) |
Mapping Pointers Array(各エントリのフォーマットは以下の通りです) | ||
0 | 4 | Owned Resource ID (所有されるResourceのResource ID) |
4 | 4 | Owner Resource ID (所有するCast MemberのResource ID) |
8 | 4 | Owned Chunk ID (所有されるResourceのChunk ID) |
CAS*(Cast Table)のフォーマット
CAS*(Cast Table)のフォーマットは、下表の通りです。
CAS*はCast IDとResource IDの対応関係を示すテーブルです。
データとしてはResource IDしか保有しておらず、エントリー番号とCast IDが1対1に対応しています(※)。
(※)次項で説明するVWCFのCast Array Startの値が1エントリー目のCast IDになります。
(CAS*(Cast Table)のフォーマット)
オフセット | 長さ | 説明 |
---|---|---|
0 | 4 | Chunk ID('CAS*’) |
4 | 4 | Data Size |
Cast Table Array(各エントリのフォーマットは以下の通りです) | ||
0 | 4 | Resource ID |
VWCF(Video Works Configuration)のフォーマット
VWCF(Video Works Configuration)のフォーマットは、下表の通りです。
(VWCF(Video Works Configuration)のフォーマット)
オフセット | 長さ | 説明 |
---|---|---|
0 | 4 | Chunk ID('VWCF’) |
4 | 4 | Data Size(X’00000050′) |
8 | 2 | Length(Data sizeと同値) |
10 | 2 | Version Number |
12 | 2 | Stage Top |
14 | 2 | Stage Left |
16 | 2 | Stage Bottom |
18 | 2 | Stage Right |
20 | 2 | Cast Array Start |
22 | 2 | Cast Array End |
24 | – | 説明省略(Frame Rate、Stage Color等) |
VWSC(Video Works Score)のフォーマット
VWSC(Video Works Score)のフォーマットは、下表の通りです。
スコアは複数のチャネルから構成され、チャネルは複数のフレームから構成されています(Adobe Directorのスコア・ウィンドウでは、行がチャネル、列がフレームのイメージになっています)。
下表の最後に記載したChannel Dataは、重要なデータを保有しているうえ、特殊な方式で圧縮されていて難解なため、次項で詳細を説明します。
(VWSC(Video Works Score)のフォーマット)
オフセット | 長さ | 説明 |
---|---|---|
0 | 4 | Chunk ID('VWSC’) |
4 | 4 | Data Size |
8 | 4 | Frames End Offset(Data sizeと同値) |
12 | 4 | Unknown |
16 | 4 | Frames Size |
20 | 2 | Frames Type |
22 | 2 | Frame Size(X’0014′) |
24 | 2 | Channel Count |
26 | 2 | Last Channel |
28 | 可変長 | Channel Data |
フレームとChannel Dataの詳細
スコアの最小単位であるフレームは20バイト(固定)ですが、チャネルの種類によって保有する項目が異なります。
基本チャネル、パレット・チャネル、スプライト・チャネルのフレーム構造は、下表の通りです。
(筆者が動作確認に使用したDirectorファイルの場合、1つ目のチャネルが基本チャネル、2つ目がパレット・チャネル、3つ目または4つ目にスプライト・チャネルが定義されていました)
(基本チャネルのフレーム構造)
オフセット | 長さ | 説明 |
---|---|---|
0 | 2 | Flags |
2 | 1 | Transition Duration |
3 | 1 | Transition Chunk Size |
4 | 1 | Frames Per Second |
5 | 1 | Transition ID |
6 | 2 | Sound1 Cast |
8 | 2 | Sound2 Cast |
10 | 2 | Sound Flags |
12 | 2 | Unknown |
14 | 2 | Unknown |
16 | 2 | Script |
18 | 2 | Unknown |
(パレット・チャネルのフレーム構造)
オフセット | 長さ | 説明 |
---|---|---|
0 | 2 | Palette ID(CLUTのCast ID) |
2 | 2 | Unknown |
4 | 1 | Operation |
5 | 1 | FPS |
6 | 2 | Unknown |
8 | 2 | Cycles |
10 | 10 | Unknown |
(スプライト・チャネルのフレーム構造)
オフセット | 長さ | 説明 |
---|---|---|
0 | 2 | Sprite Type |
2 | 1 | Foreground Color |
3 | 1 | Background Color |
4 | 1 | Flags |
5 | 1 | Ink Type |
6 | 2 | Cast Id |
8 | 2 | Y |
10 | 2 | X |
12 | 2 | Height |
14 | 2 | Width |
16 | 2 | Flag1 |
18 | 2 | Flag2 |
各チャネルのフレーム・データはX’00’の部分が多いため、VWSCに保有しているChannel DataはX’00’以外の値のみを保有する方式で、データを圧縮しています。
(VWSCに保有しているChannel Dataの形式)
オフセット | 長さ | 説明 |
---|---|---|
0 | 2 | 圧縮データのサイズ |
2 | 2 | 展開後のオフセット |
4 | 可変長 | 展開する値 |
- (Channel Dataの例)
- 00 02 00 0A FF FF
- X’0002′:圧縮データのサイズ
- X’000A’:展開後のオフセット
- X’FFFF’:展開する値
- (展開後のフレーム)
- 00 00 00 00 00 00 00 00 00 00 FF FF 00 00 00 00 00 00 00 00
CASt(Cast Member Pointers)のフォーマット
CASt(Cast Member Pointers)の内容はData Typeによって異なりますが、画像イメージ(Data Type=1)のフォーマットは、下表の通りです。
(CASt(Cast Member Pointers))
オフセット | 長さ | 説明 |
---|---|---|
0 | 4 | Chunk ID('CASt’) |
4 | 4 | Data Size |
8 | 2 | Header Size |
10 | 4 | Additional Size |
14 | 1 | Data Type 1:Image 3:Text Input 4:CLUT 6:SND 7:Push Button 8:Shape 11:LSCR 12:Text 14:Tran |
15 | 1 | Flags |
16 | 1 | BMP BPP Value X’80’:8 bit per pixel image X’81’:4 bit per pixel image X’82’:8 bit per pixel image X’84’:16 bit per pixel image X’85’:16 bit per pixel image(MAC format) X’8A’:24 bit per pixel image |
17 | 1 | Unknown |
18 | 2 | Height Padding |
20 | 2 | Width Padding |
22 | 2 | BMP Height |
24 | 2 | BMP Width |
26 | 2 | Top |
28 | 2 | Left |
30 | 2 | Bottom |
32 | 2 | Right |
34 | 2 | Center Y |
36 | 2 | Center X |
38 | 2 | Bit Depth |
40 | 2 | Palette |
42 | – | 説明省略(Cast Memberの名称等) |
CLUT(Color Lookup Table)のフォーマット
8ビット/ピクセル(256色)の場合、CLUT(Color Lookup Table)のフォーマットは、下表の通りです。
(CLUT(Color Lookup Table)のフォーマット)
オフセット | 長さ | 説明 |
---|---|---|
0 | 4 | Chunk ID('CLUT’) |
4 | 4 | Data Size(X’00000600′) |
8 | 1536 | Color Palette Data |
Color Palette Dataは、BMPファイルのRGBTRIPLEと同じような形式ですが、R→G→Bの順で2バイトずつ保有しており、1バイト目と2バイト目に同じ値が入っています。
BMPファイルのカラーパレットはB→G→Rの順に格納する仕様のため、順番を入れ替える必要があります。
BITD(Bitmap Data)のフォーマット
BITD(Bitmap Data)のフォーマットは、下表の通りです。
(BITD(Bitmap Data)のフォーマット)
オフセット | 長さ | 説明 |
---|---|---|
0 | 4 | Chunk ID('BITD’) |
4 | 4 | Data Size |
8 | 可変長 | Bitmap Data |
Bitmap Dataはランレングス圧縮(RLE:Run Length Encoding、連長圧縮)されており、レングス(1バイト)+データの形式で格納されています。
レングス≧X’80’の場合は次の1バイトを(256 – レングス + 1)バイトにセット、レングス<X’80’の場合は次のデータからレングス分のバイトを転送する方式で復元できます。
なお、ビットマップ・データは画像イメージの上→下の順に配置されているため、BMPファイルに出力する際は下→上に並べ替える必要があります。
レングス | データ | ・・・ | データ |
■Directorファイルから画像データを抽出する処理
Directorファイルを読み込み、画像データをファイルに出力するプログラムです。
⇒サンプル・プログラムで使用している、Binary Fileクラスの詳細については、Excel VBAでファイル解析(事前準備編)を参照して下さい。
⇒また、BMPファイルの詳細については、Excel VBAでファイル解析(BMPファイル編)を参照して下さい。
データ宣言部とメイン・プロシジャ
処理の流れは以下の通りです。
①mmap、KEY*、CAS*、VWSCをワーク・エリアに展開
②mmapからBITDを検索し、対応するCLUTとBITDをBMPファイルに出力
(※)今回使用したDirectorファイルはMAC版で作成されたファイルだったので、数値データ(サイズ、オフセット等)がビッグ・エンディアンで格納されていましたが、Windows版で作成されたDirectorファイルはリトル・エンディアンで格納されているため、計算に使用する場合はエンディアン変換が必要です。
- Dim bf As Object
- Dim OUTBMPpath As String
- Dim OUTBMPlen As Long
- Dim OUTBMPnum As Integer
- Dim OUTBMPbuf() As Byte
- Dim mmapChunkID() As String
- Dim mmapOffset() As Long
- Dim keyOwnedResourceID() As Long
- Dim keyOwnerResourceID() As Long
- Dim casResourceID() As Long
- Dim vwscHeight() As Integer
- Dim vwscWidth() As Integer
- Dim vwscPaletteID() As Integer
- Dim vwscOffset As Long
- Dim CastStart As Integer
- Dim CastEnd As Integer
- Dim CastID As Integer
- Dim ResourceID As Long
- Dim clut(0 To 1023) As Byte
- Dim bitd() As Byte
- Dim bitw() As Byte
- Dim bmpd() As Byte
- Dim wID As Variant
- Dim wLen As Variant
- Dim wOff As Variant
- Dim wTyp As Variant
- Dim wStr As String
- Dim fromBITD As Long
- Dim toBITD As Long
- Dim fromCLUT As Long
- Dim toCLUT As Long
- Dim EntNum As Long
- Dim EntLen As Long
- Dim FrameSize As Long
- Dim ChannelCount As Long
- Dim ChannelSize As Long
- Dim ChannelData() As Byte
- Dim DeltaSize As Long
- Dim DeltaOffset As Long
- Dim bmp_height As Long
- Dim wHeight As Variant
- Dim bmp_width As Long
- Dim bmp_wlimit As Long
- Dim wWidth As Variant
- Dim h_padding As Integer
- Dim w_padding As Integer
- Dim PaletteID As Integer
- Dim bmp_bpp As Integer
- Dim wSize As Variant
- Dim values As Long
- Dim previous_encoded As Boolean
- Dim previous_run_length As Long
- Dim wval As Byte
- Dim run_length As Long
- Dim run_value As Byte
- Dim si As Long
- Dim di As Long
- Dim i As Long
- Dim j As Long
- Dim k As Long
- Dim l As Long
- Dim w As Long
- Dim x As Long
- Dim y As Long
- Dim idx As Long
- Dim pos As Long
- Dim buf As String
- Dim wStrPath As String
- Dim wByte0, wByte1, wByte2, wByte3, wByte4, wByte5 As Byte
- Private Sub Sample1()
- Set bf = New BinaryFile
- bf.InputFile (“C:\work\xxx.DIR")
- Call mmap展開
- Call KEY展開
- Call CAS展開
- Call VWSC展開
- For i = 0 To UBound(mmapChunkID)
- If mmapChunkID(i) = “BITD" Then
- For j = 0 To UBound(keyOwnedResourceID)
- If i = keyOwnedResourceID(j) Then
- Exit For
- End If
- Next j
- If j > UBound(keyOwnedResourceID) Then
- MsgBox “BITD ResourceID is not found!"
- Exit Sub
- End If
- Call CASt抽出
- Call ヘッダ出力
- Call CLUT出力
- Call BITD出力
- End If
- Next i
- Set bf = Nothing
- End Sub
mmapを展開するサブ・プロシジャ
行番号6でmmapの開始位置を取得し、行番号14~25の繰返し処理でmmapのデータをワーク・エリアに展開しています。
- Public Sub mmap展開()
- ' [Archive Header]
- bf.SkipData (12)
- ' [Input Map]
- bf.SkipData (12)
- bf.CurrentPosition = bf.GetData(4)
- ' [Memory Map]
- bf.SkipData (16)
- EntNum = bf.GetData(4)
- ReDim mmapChunkID(0 To EntNum – 1)
- ReDim mmapOffset(0 To EntNum – 1)
- bf.SkipData (12)
- y = y + 1
- For j = 1 To EntNum
- wByte0 = bf.GetData(1)
- bf.CurrentPosition = bf.CurrentPosition – 1
- If (wByte0 >= Val(&H2A)) And (wByte0 <= Val(&H7A)) Then
- wID = bf.GetDataChar(4)
- wLen = bf.GetDataHex(4)
- wOff = bf.GetDataHex(4)
- mmapChunkID(j – 1) = wID
- mmapOffset(j – 1) = CLng(“&H" & wOff)
- bf.SkipData (8)
- End If
- Next j
- End Sub
KEY*を展開するサブ・プロシジャ
行番号11でKEY*の開始位置を取得し、行番号20~37の繰返し処理でKEY*のデータをワーク・エリアに展開しています。
- Public Sub KEY展開()
- For j = 0 To UBound(mmapChunkID)
- If mmapChunkID(j) = “KEY*" Then
- Exit For
- End If
- Next j
- If j > UBound(mmapChunkID) Then
- MsgBox “KEY* is not found!"
- Exit Sub
- End If
- bf.CurrentPosition = mmapOffset(j)
- ' [Key Mapping Pointers-Properties]
- bf.SkipData (10)
- EntLen = bf.GetData(2)
- EntNum = bf.GetData(4)
- bf.SkipData (4)
- ReDim keyOwnedResourceID(0 To EntNum – 1)
- ReDim keyOwnerResourceID(0 To EntNum – 1)
- ' [Key Mapping Pointers-Mapping Pointers Array]
- For j = 1 To EntNum
- wByte0 = bf.GetData(1)
- bf.SkipData (3)
- wByte1 = bf.GetData(1)
- bf.SkipData (3)
- wByte2 = bf.GetData(1)
- wByte3 = bf.GetData(1)
- wByte4 = bf.GetData(1)
- wByte5 = bf.GetData(1)
- bf.CurrentPosition = bf.CurrentPosition – 12
- If (wByte0 < Val(&H80)) And (wByte1 < Val(&H70)) And (wByte2 >= Val(&H2A)) And (wByte2 <= Val(&H7A)) And (wByte3 >= Val(&H2A)) And (wByte3 <= Val(&H7A)) And (wByte4 >= Val(&H2A)) And (wByte4 <= Val(&H7A)) And (wByte5 >= Val(&H2A)) And (wByte5 <= Val(&H7A)) Then
- keyOwnedResourceID(j – 1) = bf.GetData(4)
- keyOwnerResourceID(j – 1) = bf.GetData(4)
- bf.SkipData (4)
- Else
- bf.SkipData (EntLen)
- End If
- Next j
- End Sub
CAS*を展開するサブ・プロシジャ
行番号11でVWCFの開始位置を取得し、行番号14~15でCast IDの開始/終了を把握したうえ、行番号26でCAS*の開始位置を取得し、行番号31~33の繰返し処理でCAS*のデータをワーク・エリアに展開しています。
- Public Sub CAS展開()
- For j = 0 To UBound(mmapChunkID)
- If mmapChunkID(j) = “VWCF" Then
- Exit For
- End If
- Next j
- If j > UBound(mmapChunkID) Then
- MsgBox “VWCF is not found!"
- Exit Sub
- End If
- bf.CurrentPosition = mmapOffset(j)
- ' [Video Works Configuration]
- bf.SkipData (20)
- CastStart = bf.GetData(2)
- CastEnd = bf.GetData(2)
- ReDim casResourceID(0 To CastEnd)
- For j = 0 To UBound(mmapChunkID)
- If mmapChunkID(j) = “CAS*" Then
- Exit For
- End If
- Next j
- If j > UBound(mmapChunkID) Then
- MsgBox “CAS* is not found!"
- Exit Sub
- End If
- bf.CurrentPosition = mmapOffset(j)
- ' [Cast Mapping Pointers]
- bf.SkipData (4)
- wLen = bf.GetDataHex(4)
- EntNum = (CLng(“&H" & wLen) – 1) / 4
- For j = 1 To EntNum
- casResourceID(CastStart + j – 1) = bf.GetData(4)
- Next j
- End Sub
VWSCを展開するサブ・プロシジャ
行番号11でVWSCの開始位置を取得し、行番号32~68でChannel Dataを展開したうえ、Height、Width、Palette IDをワーク・エリアに退避しています。
- Public Sub VWSC展開()
- For j = 0 To UBound(mmapChunkID)
- If mmapChunkID(j) = “VWSC" Then
- Exit For
- End If
- Next j
- If j > UBound(mmapChunkID) Then
- MsgBox “VWSC is not found!"
- Exit Sub
- End If
- bf.CurrentPosition = mmapOffset(j)
- vwscOffset = mmapOffset(j)
- ReDim vwscHeight(0 To CastEnd)
- ReDim vwscWidth(0 To CastEnd)
- ReDim vwscPaletteID(0 To CastEnd)
- For j = 0 To UBound(vwscHeight)
- vwscHeight(j) = 0
- vwscWidth(j) = 0
- vwscPaletteID(j) = 0
- Next j
- ' [Video Works Score]
- bf.SkipData (4)
- wLen = bf.GetDataHex(4)
- bf.SkipData (14)
- FrameSize = bf.GetData(2)
- ChannelCount = bf.GetData(2)
- bf.SkipData (2)
- ReDim ChannelData(0 To ChannelCount * FrameSize – 1)
- For j = 0 To UBound(ChannelData)
- ChannelData(j) = 0
- Next j
- Do While bf.CurrentPosition < vwscOffset + CLng(“&H" & wLen) + 8
- ' [Channel Data]"
- ChannelSize = bf.GetData(2) – 2
- Do While ChannelSize > 0
- DeltaSize = bf.GetData(2)
- DeltaOffset = bf.GetData(2)
- ChannelSize = ChannelSize – 4
- For j = 1 To DeltaSize
- ChannelData(DeltaOffset + j – 1) = bf.GetData(1)
- ChannelSize = ChannelSize – 1
- Next j
- Loop
- If (Right(“0" & Hex(ChannelData(46)), 2) & Right(“0" & Hex(ChannelData(47)), 2)) <> “0000" Then
- CastID = CInt(“&H" & Right(“0" & Hex(ChannelData(46)), 2) & Right(“0" & Hex(ChannelData(47)), 2))
- If vwscHeight(CastID) = 0 Then
- vwscHeight(CastID) = CInt(“&H" & Right(“0" & Hex(ChannelData(52)), 2) & Right(“0" & Hex(ChannelData(53)), 2))
- End If
- If vwscWidth(CastID) = 0 Then
- vwscWidth(CastID) = CInt(“&H" & Right(“0" & Hex(ChannelData(54)), 2) & Right(“0" & Hex(ChannelData(55)), 2))
- End If
- If vwscPaletteID(CastID) = 0 Then
- vwscPaletteID(CastID) = CInt(“&H" & Right(“0" & Hex(ChannelData(20)), 2) & Right(“0" & Hex(ChannelData(21)), 2))
- End If
- End If
- If (Right(“0" & Hex(ChannelData(66)), 2) & Right(“0" & Hex(ChannelData(67)), 2)) <> “0000" Then
- CastID = CInt(“&H" & Right(“0" & Hex(ChannelData(66)), 2) & Right(“0" & Hex(ChannelData(67)), 2))
- If vwscHeight(CastID) = 0 Then
- vwscHeight(CastID) = CInt(“&H" & Right(“0" & Hex(ChannelData(72)), 2) & Right(“0" & Hex(ChannelData(73)), 2))
- End If
- If vwscWidth(CastID) = 0 Then
- vwscWidth(CastID) = CInt(“&H" & Right(“0" & Hex(ChannelData(74)), 2) & Right(“0" & Hex(ChannelData(75)), 2))
- End If
- If vwscPaletteID(CastID) = 0 Then
- vwscPaletteID(CastID) = CInt(“&H" & Right(“0" & Hex(ChannelData(20)), 2) & Right(“0" & Hex(ChannelData(21)), 2))
- End If
- End If
- Loop
- End Sub
CAStを抽出するサブ・プロシジャ
行番号2~7でBITDのResource IDでKEY*を検索してCAStのResource IDを取得、行番号12~17でCAStのResource IDでCAS*を検索してCast IDを取得した後、行番号22でCAStの開始位置を取得し、行番号26~42でHeight、Width、Palette IDを取得しています。
BMPファイルに使用するHeight、Widthは、パディング分を差し引く必要があるため、行番号33、35で減算処理を行っています。
また、Height、Width、Palette IDは、CAStとVWSC(Channel Data)に格納可能で、通常は「どちらか一方に格納」または「両方に同じ値が格納」されていますが、中には両方に値を保有し、かつ値が異なっているケースがあり、その場合にはChannel Dataの方を優先する必要があります(行番号43~51でChannel Dataの値で上書きしています)。
- Public Sub CASt抽出()
- For j = 0 To UBound(keyOwnedResourceID)
- If keyOwnedResourceID(j) = i Then
- ResourceID = keyOwnerResourceID(j)
- Exit For
- End If
- Next j
- If j > UBound(keyOwnedResourceID) Then
- MsgBox “CASt(KEY*) is not found!"
- Exit Sub
- End If
- For j = 0 To UBound(casResourceID)
- If casResourceID(j) = ResourceID Then
- CastID = j
- Exit For
- End If
- Next j
- If j > UBound(casResourceID) Then
- MsgBox “CASt(CAS*) is not found!"
- Exit Sub
- End If
- bf.CurrentPosition = mmapOffset(ResourceID)
- ' [Cast Member Pointers]
- bf.SkipData (14)
- wTyp = bf.GetDataHex(1)
- If wTyp = “01" Then
- bf.SkipData (3)
- wStr = Right(“000" & bf.GetDataHex(2), 4)
- h_padding = CInt(“&H" & wStr)
- wStr = Right(“000" & bf.GetDataHex(2), 4)
- w_padding = CInt(“&H" & wStr)
- bmp_height = bf.GetData(2)
- bmp_height = bmp_height – h_padding
- bmp_width = bf.GetData(2)
- bmp_width = bmp_width – w_padding
- If (bmp_width Mod 2) <> 0 Then
- bmp_width = bmp_width + 1
- End If
- bf.SkipData (14)
- wStr = Right(“000" & bf.GetDataHex(2), 4)
- PaletteID = CInt(“&H" & wStr)
- End If
- If (vwscHeight(CastID) <> 0) And (bmp_height = 0) Then
- bmp_height = vwscHeight(CastID)
- End If
- If (vwscWidth(CastID) <> 0) And (bmp_width = 0) Then
- bmp_width = vwscWidth(CastID)
- End If
- If vwscPaletteID(CastID) <> 0 Then
- PaletteID = vwscPaletteID(CastID)
- End If
- End Sub
BMPファイルのヘッダを作成するサブ・プロシジャ
ワーク・エリアに展開された情報を基にBMPファイルのヘッダを作成しています。
- Public Sub ヘッダ出力()
- OUTBMPpath = “C:\work\xxx" & “-" & Format(CastID, “000") & “.bmp"
- bmp_wlimit = bmp_width
- If bmp_width Mod 4 <> 0 Then
- bmp_width = bmp_width + 4 – (bmp_width Mod 4)
- End If
- If bmp_wlimit Mod 2 <> 0 Then
- bmp_wlimit = bmp_wlimit + 1
- End If
- bmp_bpp = 8
- values = 14 + 40 + (256 * 4) + bmp_width * bmp_height
- If values Mod 2 <> 0 Then
- values = values + 1
- End If
- wLen = Right(“0000000" & Hex(values), 8)
- OUTBMPlen = values
- If OUTBMPlen Mod 2 <> 0 Then
- OUTBMPlen = OUTBMPlen + 1
- End If
- OUTBMPnum = FreeFile
- Open OUTBMPpath For Binary Access Write As #OUTBMPnum
- ReDim OUTBMPbuf(0 To OUTBMPlen – 1)
- di = 0
- OUTBMPbuf(di) = &H42
- di = di + 1
- OUTBMPbuf(di) = &H4D
- wStr = bf.ConvertEndian(wLen)
- For j = 1 To 7 Step 2
- di = di + 1
- OUTBMPbuf(di) = CByte(“&H" & Mid(wStr, j, 2))
- Next j
- For j = 1 To 4
- di = di + 1
- OUTBMPbuf(di) = &H0
- Next j
- di = di + 1
- OUTBMPbuf(di) = &H36
- di = di + 1
- OUTBMPbuf(di) = &H4
- For j = 1 To 2
- di = di + 1
- OUTBMPbuf(di) = &H0
- Next j
- di = di + 1
- OUTBMPbuf(di) = &H28
- For j = 1 To 3
- di = di + 1
- OUTBMPbuf(di) = &H0
- Next j
- wWidth = Right(“0000000" & Hex(bmp_width), 8)
- wStr = bf.ConvertEndian(wWidth)
- For j = 1 To 7 Step 2
- di = di + 1
- OUTBMPbuf(di) = CByte(“&H" & Mid(wStr, j, 2))
- Next j
- wHeight = Right(“0000000" & Hex(bmp_height), 8)
- wStr = bf.ConvertEndian(wHeight)
- For j = 1 To 7 Step 2
- di = di + 1
- OUTBMPbuf(di) = CByte(“&H" & Mid(wStr, j, 2))
- Next j
- di = di + 1
- OUTBMPbuf(di) = &H1
- di = di + 1
- OUTBMPbuf(di) = &H0
- di = di + 1
- OUTBMPbuf(di) = &H8
- di = di + 1
- OUTBMPbuf(di) = &H0
- For j = 1 To 4
- di = di + 1
- OUTBMPbuf(di) = &H0
- Next j
- wSize = Right(“0000000" & Hex(bmp_width * bmp_height), 8)
- wStr = bf.ConvertEndian(wSize)
- For j = 1 To 7 Step 2
- di = di + 1
- OUTBMPbuf(di) = CByte(“&H" & Mid(wStr, j, 2))
- Next j
- For j = 1 To 16
- di = di + 1
- OUTBMPbuf(di) = &H0
- Next j
- End Sub
BMPファイルのカラー・パレットを作成するサブ・プロシジャ
ワーク・エリアに展開された情報を基にBMPファイルのカラー・パレットを作成しています。
行番号30でCALLしているシステム・パレットの処理については、次項で説明します。
- Public Sub CLUT出力()
- If PaletteID > 0 Then
- For j = 0 To UBound(keyOwnerResourceID)
- If keyOwnerResourceID(j) = casResourceID(PaletteID) Then
- ResourceID = keyOwnedResourceID(j)
- Exit For
- End If
- Next j
- If j > UBound(keyOwnerResourceID) Then
- MsgBox “CLUT(KEY*) is not found!"
- Exit Sub
- End If
- bf.CurrentPosition = mmapOffset(ResourceID)
- fromCLUT = mmapOffset(ResourceID) + 8
- bf.SkipData (4)
- wLen = bf.GetDataHex(4)
- toCLUT = fromCLUT + CLng(“&H" & wLen) – 1
- k = 0
- For j = fromCLUT To toCLUT Step 6
- clut(k + 2) = bf.GetData(1)
- bf.SkipData (1)
- clut(k + 1) = bf.GetData(1)
- bf.SkipData (1)
- clut(k) = bf.GetData(1)
- bf.SkipData (1)
- clut(k + 3) = &H0
- k = k + 4
- Next j
- Else
- Call SystemPalette取得
- End If
- For j = 0 To 1023
- di = di + 1
- OUTBMPbuf(di) = clut(j)
- Next j
- End Sub
システム・パレットを作成するサブ・プロシジャ
Palette IDが負の値の場合はシステム・パレットが指定されており、CLUTが存在しないため、CLUTに相当する値を返す関数を作成しました(この関数の部分は煩雑になるため、テキスト・ファイルを添付しておきます)。
- Public Sub SystemPalette取得()
- Select Case PaletteID
- ' systemMac
- Case -1
- Call SystemPalette_systemMac
- ' rainbow
- Case -2
- Call SystemPalette_rainbow
- ' grayscale
- Case -3
- Call SystemPalette_grayscale
- ' pastels
- Case -4
- Call SystemPalette_pastels
- ' vivid
- Case -5
- Call SystemPalette_vivid
- ' ntsc
- Case -6
- Call SystemPalette_ntsc
- ' metallic
- Case -7
- Call SystemPalette_metallic
- ' systemWinDir4
- Case -101
- Call SystemPalette_systemWinDir4
- ' systemWin
- Case -102
- Call SystemPalette_systemWin
- ' systemMac
- Case Else
- Call SystemPalette_systemMac
- End Select
- End Sub
↓System Paletteの値を返す関数
SystemPalette.txt
BMPファイルのビットマップ・データを作成し、BMPファイルを出力するサブ・プロシジャ
行番号6でBITDの開始位置を取得し、行番号30~61の繰り返し処理でランレングス圧縮されたデータを復元し、行番号68~79の繰り返し処理でBMPファイル用に画像データを下から上に向って並べ替えたうえ、行番号85でBMPファイルを出力しています。
- Public Sub BITD出力()
- ReDim bmpd(0 To bmp_width * bmp_height – 1)
- For k = 0 To UBound(bmpd)
- bmpd(k) = 0
- Next k
- bf.CurrentPosition = mmapOffset(i)
- fromBITD = mmapOffset(i) + 8
- bf.SkipData (4)
- wLen = bf.GetDataHex(4)
- toBITD = fromBITD + CLng(“&H" & wLen) – 1
- ReDim bitd(0 To toBITD – fromBITD)
- bf.CurrentPosition = fromBITD
- For j = 0 To UBound(bitd)
- bitd(j) = bf.GetData(1)
- Next j
- ReDim bitw(0 To bmp_width * bmp_height – 1)
- For j = 0 To UBound(bitw)
- bitw(j) = 0
- Next j
- idx = 0
- si = 0
- If (bmp_height * bmp_wlimit) <= (UBound(bitd) + 1) Then
- Do While idx < UBound(bitw)
- bitw(idx) = bitd(idx)
- idx = idx + 1
- si = si + 1
- Loop
- Else
- Do While (idx < UBound(bitd)) And (si < UBound(bitw))
- wval = bitd(idx)
- If wval >= 128 Then
- run_length = 256 – wval + 1
- If idx + 1 >= UBound(bitd) Then
- Exit Do
- End If
- run_value = bitd(idx + 1)
- idx = idx + 2
- For k = 1 To run_length
- bitw(si) = run_value
- si = si + 1
- If si > UBound(bitw) Then
- Exit Do
- End If
- Next k
- Else
- run_length = wval + 1
- idx = idx + 1
- If idx >= UBound(bitd) Then
- Exit Do
- End If
- For k = 1 To run_length
- bitw(si) = bitd(idx)
- idx = idx + 1
- si = si + 1
- If si > UBound(bitw) Then
- Exit Do
- End If
- Next k
- End If
- Loop
- End If
- x = 0
- y = bmp_height – 1
- w = bmp_width
- idx = 0
- Do While (idx < UBound(bitw)) And (y >= 0)
- bmpd(y * w + x) = bitw(idx)
- idx = idx + 1
- x = x + 1
- If x >= bmp_wlimit Then
- x = 0
- y = y – 1
- If y < 0 Then
- Exit Do
- End If
- End If
- Loop
- For k = 0 To UBound(bmpd)
- OUTBMPbuf(di) = bmpd(k)
- di = di + 1
- Next k
- Put #OUTBMPnum, , OUTBMPbuf
- Close #OUTBMPnum
- End Sub
(おまけ)Adobe Directorに関する書籍について
Adobe Directorに関する書籍を探したところ、インドネシアのサイトで以下の書籍が紹介されているのを発見。
さらにネットで検索しまくった結果、イギリスのAmazonで取扱っていたので購入しました。
- 著者:Klemens Hübner
- 書名:Macromedia Director Multimediaprogrammierung mit Lingo
- 出版社:Springer-Verlag
- 発行日:2013/10/3
- 補足:Softcover reprint of the original 1st ed. 2004版
先程、改めて調べたところ、日本のAmazonでも購入できるようなので、興味のある方はチェックしてみて下さい。
(ドイツ語で書かれているため、正確に理解するのは困難ですが、図版が多いので大体は理解できると思います)
Macromedia Director Multimediaprogrammierung mit Lingo
出版社:インプレス
発売日:2022/3/23
単行本(ソフトカバー):A5判/912ページ
出版社:技術評論社
発売日:2021/1/9
単行本(ソフトカバー):A5判/800ページ
出版社:技術評論社
発売日:2019/11/25
単行本(ソフトカバー):B5変形判/576ページ
ディスカッション
コメント一覧
まだ、コメントがありません