A THINKING FRUIT
概要
 ダンプ構造にのっとってファイルをダンプする。
 コードは Windows に依存していない。
注意
 設定ファイルにそぐわない内容のファイルをダンプした場合、例外が発生する。
使用例
使用ファイル一覧
1.dummy.bin ダンプ対象ファイル。
2.Dump-X-V2-Lib.ps1 本ツールの本体(PowerShell ファイル)。
3.View-Dummy.ps1 ダンプ構造を定義したファイル、兼、ダンプ実行ファイル(PowerShell ファイル)。
4.dump.bin.txt 出力ファイル。※ダンプ結果。実行すると出力する(UTF-8)。
5.00001_りんご.bin 出力ファイル。※ダンプを実行すると出力する(ダンプ構造の内容による)。
実行方法
PS D:\dev\Dump-X-V2> .\View-Dummy.ps1 .\dummy.bin .\dummy.bin.txt
実行結果(dummy.bin.txt)
00000000 chunkId: "HEAD" 00000004 chunkSize: 26225 00000006 nColorCount: 2 0000000A R: F0 0000000B G: F1 0000000C B: F2 0000000D A: 10 0000000E R: A9 0000000F G: A8 00000010 B: A7 00000011 A: 20 00000012 StreamBytes: 16 00000016 nStream: FF FE FD FC FB FA F9 F8 F7 F6 F5 F4 F3 F2 F1 F0 00000026 TextSJIS: "りんご" 0000002C TextUTF16: "東京" 00000030 bin: 9 (D:\dev\Dump-X-V2\00001_りんご.bin)
ダンプ対象ファイル
00000000 48 45 41 44 71 66 02 00 00 00 F0 F1 F2 10 A9 A8
00000010 A7 20 10 00 00 00 FF FE FD FC FB FA F9 F8 F7 F6
00000020 F5 F4 F3 F2 F1 F0 82 E8 82 F1 82 B2 71 67 AC 4E
00000030 B1 B2 B3 B4 B5 B6 B7 B8 B9
ダンプ対象ファイルの内容(C/C++ 構造体で示す)
struct Color_t { unsigned char R; unsigned char G; unsigned char B; unsigned char A; }; struct Dummy_t { char chunkId[4]; short chunkSize; long nColorCount; Color_t color[nColor]; long nStreamBytes; unsigned char nStream[nStreamBytes]; unsigned char TextSJIS[3 * 2]; unsigned char TextUTF16[2 * 2]; unsigned char bin[9]; };
ダンプ構造を定義したファイル(View-Dummy.ps1 ファイル)の内容
Param( [String] $File, [String] $OutputFile ) . .\Dump-X-V2-Lib.ps1 $fnFileSave = ` { Param( $infoFile, $info, $members, $member ) [System.IO.DirectoryInfo] $dir = Get-Item . $info.nFileIndex ++; $strFileName = '{0:D05}_{1}.bin' -f $info.nFileIndex ,$info.FileName # 出力ファイル名 $infoFile.strSavefilePath = [System.IO.Path]::Combine( $dir.FullName, $strFileName ); }.GetNewClosure()
# # fn # データの読み取り方法を指定する。指定できる方法を以下に示す。 # 未定義の場合は 'FC-READ-BYTES' となる。 # # 'FC-DISP-I'  整数として読み取り出力する。 # 'FC-DISP-S'  文字列(ASCII)を読み取り出力する。 # # 'FC-READ-I'  整数として読み取り格納し出力する。 # 読み取った整数は pm=@{strMemberName='~'} で指定した info のメンバへ格納する。 # info[pm.strMemberName] = n # # 'FC-READ-S'  文字列として読み取り格納し出力する。 # 読み取った文字列は pm=@{strMemberName='~'} で指定した info のメンバへ格納する。 # info[pm.strMemberName] = s # # 'FC-READ-BYTES' バイト配列として読み取り格納し出力する。 # 読み取ったバイト配列は pm=@{strMemberName='~'} で指定した info のメンバへ格納する。 # info[pm.strMemberName] = stream # # 'FC-FILE-SAVE' バイト配列として読み取りファイルセーブ用コールバックで取得したファイルへ出力する。 # # 'FC-STRUCT-READ-I' 整数として読み取り格納し出力する。 # 読み取った整数は pm=@{strMemberName='~'} で指定した storage(※)のメンバへ格納する。 # storage[pm.strMemberName] = n # ※storage は Dump-X-V2 の第 1 引数に指定したデータ構造のメンバ # # fnInit 配列 # データを読み取る前に行う処理を指定する。指定できる処理を以下に示す。 # 未定義の場合は何も行わない。 # # 'FC-SET-SIZE'  'FC-READ_I' で取得した値を size へ設定する。 # 'FC-SET-LOOP'  'FC-READ_I' で取得した値を loop へ設定する。 # # 'FC-STRUCT-SET-SIZE' 'FC-STRUCT-READ-I' で取得した値を size へ設定する。 # 'FC-STRUCT-SET-LOOP' 'FC-STRUCT-READ-I' で取得した値を loop へ設定する。 # #
$header = @{ info = @{ }; members = @( @{ name='chunkId'; size=4; loop=0; subMember=$Null; fn='FC-DISP-S'; pm=@{} } @{ name='chunkSize'; size=2; loop=0; subMember=$Null; fn='FC-DISP-I'; pm=@{} } ); } $color = @{ info = @{ }; members = @( @{ name='R'; size=1; loop=0; subMember=$Null } @{ name='G'; size=1; loop=0; subMember=$Null } @{ name='B'; size=1; loop=0; subMember=$Null } @{ name='A'; size=1; loop=0; subMember=$Null } ); } $palette = @{ info = @{ nNumberOfColor=0 }; members = @( @{ name='nColorCount'; size=4; loop=0; fn='FC-READ-I'; pm=@{strMemberName='nNumberOfColor'} } @{ name='color'; size=0; loop=0; subMember=$color; pm=@{strMemberName='nNumberOfColor'} fnInit=@( 'FC-SET-LOOP' ) } ); } $body = @{ info = @{ nBodySize = 0 FileName = 'dummy' # ファイル保存で使用 nFileIndex = 0 # ファイル保存で使用 }; members = @( @{ name='StreamBytes'; size=4; loop=0; fn='FC-READ-I'; pm=@{strMemberName='nBodySize'} } @{ name='nStream'; size=0; loop=0; pm=@{strMemberName='nBodySize'} fnInit=@( 'FC-SET-SIZE' ) } @{ name='TextSJIS'; size=6; loop=0; fn='FC-READ-S'; pm=@{strMemberName='FileName'} } @{ name='TextUTF16'; size=4; loop=0; fn='FC-DISP-UTF16'; pm=@{} } @{ name='bin'; size=9; loop=0; fn='FC-FILE-SAVE'; pm=@{ fnFileSave=$fnFileSave } } ); } $struct = @{ storage = @{ }; info = @{ }; members = @( @{ name='header'; size=0; loop=0; subMember=$header }, @{ name='palette'; size=0; loop=0; subMember=$palette }, @{ name='body'; size=0; loop=0; subMember=$body } ); } Dump-X-V2 $struct $File $OutputFile
ツール本体(Dump-X-V2-Lib.ps1)
############################################################################### # Dump-X から流用 ############################################################################### $DISP_S16 = [System.BitConverter]::ToInt16 $DISP_S32 = [System.BitConverter]::ToInt32 $DISP_S64 = [System.BitConverter]::ToInt64 $DISP_U16 = [System.BitConverter]::ToSInt16 $DISP_U32 = [System.BitConverter]::ToSInt32 $DISP_U64 = [System.BitConverter]::ToSInt64 $DISP_F32 = [System.BitConverter]::ToSingle $DISP_F64 = [System.BitConverter]::ToDouble Class Utility { static [Int32] ToInt8( [byte[]] $buffer, $bufferIndex ) { return ($buffer[$bufferIndex] -bor 0xffffff00) } static [UInt32] ToUInt8( [byte[]] $buffer, $bufferIndex ) { return ($buffer[$bufferIndex] -band 0x000000ff) } } $DISP_S8 = [Utility]::ToInt8 $DISP_U8 = [Utility]::ToUInt8 ############################################################################### ############################################################################### Class DumpX2Context { [System.IO.FileStream] $fs = $Null [System.IO.BinaryReader] $br = $Null [System.IO.BinaryWriter] $bw = $Null $out = @{ fs = [System.IO.FileStream] $Null sw = [System.IO.StreamWriter] $Null } $struct = $Null $errorInfo = @{ level=0; memberIndex=0; loopIndex=0; initIndex=0 } $nOffsetAddress = 0 $BufferBytes = 0 $Buffers = $Null [void] AllocBuffer( [Int32] $nBytes ) { if( $this.BufferBytes -lt $nBytes ) { try { $this.BufferBytes = $nBytes $this.Buffers = [Byte[]]::new( $nBytes ) } catch { Write-Error 'メモリ確保に失敗しました。' Throw } } } } Function Dump-X-V2-OpenBinary( $infoFile, $bitMode, $strPath ) { try { $strBackupDirectory = [System.IO.Directory]::GetCurrentDirectory() $strLocation = Get-Location [System.IO.Directory]::SetCurrentDirectory( $strLocation ) [System.IO.FileStream] $fs = $Null switch( $bitMode ) { 0x00 { $fs = [System.IO.File]::Open( $strPath, [System.IO.FileMode]::Open, [System.IO.FileAccess]::Read, [System.IO.FileShare]::Read ) $infoFile.br = [System.IO.BinaryReader]::new( $fs ) } 0x01 { $fs = [System.IO.File]::Open( $strPath, [System.IO.FileMode]::Create, [System.IO.FileAccess]::Write, [System.IO.FileShare]::Read ) $infoFile.bw = [System.IO.BinaryWriter]::new( $fs ) } 0x11 { $fs = [System.IO.File]::Open( $strPath, [System.IO.FileMode]::Create, [System.IO.FileAccess]::Write, [System.IO.FileShare]::Read ) $infoFile.sw = [System.IO.StreamWriter]::new( $fs ) } _ { Write-Error '引数が不正です。' -ErrorAction Stop } } $infoFile.fs = $fs } catch { $infoFile.fs = $Null Throw } finally { [System.IO.Directory]::SetCurrentDirectory( $strBackupDirectory ) } } Function Dump-X-V2-CloseBinary( $infoFile ) { if( $infoFile.br -ne $Null ) { $infoFile.br.Close() $infoFile.br = $Null } if( $infoFile.bw -ne $Null ) { $infoFile.bw.Close() $infoFile.bw = $Null } if( $infoFile.sw -ne $Null ) { $infoFile.sw.Close() $infoFile.sw = $Null } if( $infoFile.fs -ne $Null ) { $infoFile.fs.Close() $infoFile.fs = $Null } } Function X-ASCII( [Int32] $n ) { if( ($n -lt 0x20) -bor ($n -gt 0x7e) ) { return 0x2e } return $n } Function FC-DISP-DUMP( $ctx, $info, $members, $member ) { try { Display-OffsetAddress $ctx $ctx.nOffsetAddress += $member.size $ctx.out.sw.WriteLine( ('{0} ({1}):' -f $member.name ,$member.size) ) # 0 10 20 30 40 50 60 70 # *----+----*----+----*----+----*----+----*----+----*----+----*----+----*----+ # $str = [System.Text.StringBuilder]::new( "`t00000000 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................" ) $str = [System.Text.StringBuilder]::new( 76, 76 ) $nSize = $member.size $nAddress = 0 while( $nSize -gt 0 ) { $n = $nSize if( $n -gt 16 ) { $n = 16 } $nSize -= $n [void] $ctx.br.Read( $ctx.buffers, 0, $n ) [void] $str.Clear() # [void] $str.Append( ("`t{0:X08} 00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00 ................" -f $nAddress) ) [void] $str.Append( ("`t{0:X08} - " -f $nAddress) ) for( $i = 0; $i -lt $n; $i ++ ) { $strHex = $ctx.buffers[$i].ToString('X02') $str[11 + $i * 3 + 0] = $strHex[0] $str[11 + $i * 3 + 1] = $strHex[1] $str[60 + $i] = X-ASCII $ctx.buffers[$i] } if( $n -lt 9 ) { $str[34] = ' ' } $strOutput = $str.ToString() $ctx.out.sw.WriteLine( $strOutput ) $nAddress += $n } } catch { Throw } } # BinaryReader の内容でファイルを作成する Function Dump-X-V2-PutBinary( $strPath, [System.IO.Stream] $br, [Int32] $size ) { $infoFile = @{} try { try { $strError = '出力ファイルのオープンに失敗しました。' Dump-X-V2-OpenBinary $infoFile 0x01 $strPath $strError = 'メモリ確保に失敗しました。' $bufferBytes = $size if( $bufferBytes -gt 8192 ) { $bufferBytes = 8192 } $buffer = [byte[]]::new( $bufferBytes ) } catch { Write-Error $strError Throw } do { $nReadSize = $size if( $nReadSize -gt $bufferBytes ) { $nReadSize = $bufferBytes } $size -= $nReadSize [void] $br.Read( $buffer, 0, $nReadSize ) $infoFile.bw.Write( $buffer, 0, $nReadSize ) }while( $size -gt 0 ) } catch { Throw } finally { Dump-X-V2-CloseBinary $infoFile } } Function Dump-X-V2-Setup( $ctx, $strInputPath, $strOutputPath ) { try { $strError = '入力' $r = Dump-X-V2-OpenBinary $ctx 0x00 $strInputPath $strError = '出力' $r = Dump-X-V2-OpenBinary $ctx.out 0x11 $strOutputPath } catch { Dump-X-V2-Cleanup $ctx Write-Error ("{0}ファイルのオープンに失敗しました。" -f $strError) Throw } } Function Dump-X-V2-Cleanup( $ctx ) { Dump-X-V2-CloseBinary $ctx.out Dump-X-V2-CloseBinary $ctx } Function Display-OffsetAddress( $ctx ) { $ctx.out.sw.Write( $ctx.nOffsetAddress.ToString('X08') + ' ' ) } # 指定された info のメンバを member.size に設定する Function FC-SET-SIZE( $ctx, $info, $members, $member ) { $n = $info[$member.pm.strMemberName] $member.size = $n } # 指定された struct.storage のメンバを member.size に設定する Function FC-STRUCT-SET-SIZE( $ctx, $info, $members, $member ) { $member.size = $ctx.struct.storage[$member.pm.strMemberName] } # 指定された info のメンバを member.loop に設定する Function FC-SET-LOOP( $ctx, $info, $members, $member ) { $n = $info[$member.pm.strMemberName] $member.loop = $n } # 指定された struct.storage のメンバを member.loop に設定する Function FC-STRUCT-SET-LOOP( $ctx, $info, $members, $member ) { $member.loop = $ctx.struct.storage[$member.pm.strMemberName] } # コールバックを実行する Function FC-CALLBACK( $ctx, $info, $members, $member ) { & $member.pm.fnCallback $info $members $member } # 別ファイル内容を出力 # コールバック関数を呼び出し、出力ファイルのパスを取得する # コールバック関数は $member.pm.fnFileSave 。引数は連想配列、info、members、member # コールバック関数の処理は引数の連想配列に [String] strSavefilePath を設け # 出力ファイルのパスを設定すること Function FC-FILE-SAVE( $ctx, $info, $members, $member ) { Display-OffsetAddress $ctx $ctx.nOffsetAddress += $member.size $infoFile = @{} try { & $member.pm.fnFileSave $infoFile $info $members $member } catch { Write-Error 'FC-FILE-SAVE 用コールバックで例外が発生しました。' Throw } Dump-X-V2-PutBinary $infoFile.strSavefilePath $ctx.br.BaseStream $member.size $ctx.out.sw.WriteLine( ('{0}: {1} ({2})' -f $member.name ,$member.size ,$infoFile.strSavefilePath) ) } Function Inner-Read-S( [ref] $rv, $ctx, $info, $members, $member, $mode ) { Display-OffsetAddress $ctx $ctx.nOffsetAddress += $member.size $ctx.AllocBuffer( $member.size ) [void] $ctx.br.Read( $ctx.Buffers, 0, $member.size ) switch( $mode ) { 0 { $rv.Value = [System.Text.ASCIIEncoding]::Default.GetString( $ctx.Buffers, 0, $member.size ) } 1 { $rv.Value = [System.Text.UnicodeEncoding]::Unicode.GetString( $ctx.Buffers, 0, $member.size ) } _ { Write-Error '引数が不正です。' -ErrorAction Stop } } $ctx.out.sw.WriteLine( $member.name + ': "' + $rv.Value + '"' ) } Function FC-DISP-S( $ctx, $info, $members, $member ) { $rv = [String]::Empty $r = Inner-Read-S ([ref]$rv) $ctx $info $members $member 0 } Function FC-READ-S( $ctx, $info, $members, $member ) { $rv = [String]::Empty Inner-Read-S ([ref]$rv) $ctx $info $members $member 0 $info[$member.pm.strMemberName] = $rv } Function FC-DISP-UTF16( $ctx, $info, $members, $member ) { $rv = [String]::Empty Inner-Read-S ([ref]$rv) $ctx $info $members $member 1 } Function FC-READ-UTF16( $ctx, $info, $members, $member ) { $rv = [String]::Empty Inner-Read-S ([ref]$rv) $ctx $info $members $member 1 $info[$member.pm.strMemberName] = $rv } Function Inner-Read-I( [ref] $rv, $ctx, $info, $members, $member ) { Display-OffsetAddress $ctx $ctx.nOffsetAddress += $member.size [void] $ctx.br.Read( $ctx.Buffers, 0, $member.size ) switch( $member.size ) { 1 { $rv.Value = $DISP_S8.Invoke( $ctx.Buffers, 0 ) } 2 { $rv.Value = $DISP_S16.Invoke( $ctx.Buffers, 0 ) } 4 { $rv.Value = $DISP_S32.Invoke( $ctx.Buffers, 0 ) } _ { Write-Error '引数が不正です。' -ErrorAction Stop } } $ctx.out.sw.WriteLine( $member.name + ': ' + $rv.Value ) } Function FC-DISP-I( $ctx, $info, $members, $member ) { $rv = 0 Inner-Read-I ([ref]$rv) $ctx $info $members $member } Function FC-READ-I( $ctx, $info, $members, $member ) { # $member.size バイト分ファイルから整数として取得する $rv = 0 Inner-Read-I ([ref]$rv) $ctx $info $members $member $info[$member.pm.strMemberName] = $rv } Function FC-STRUCT-READ-I( $ctx, $info, $members, $member ) { $rv = 0 Inner-Read-I ([ref]$rv) $ctx $info $members $member $ctx.struct.storage[$member.pm.strMemberName] = $rv } Function FC-READ-F( $ctx, $info, $members, $member ) { # $member.size バイト分ファイルから実数として取得する Display-OffsetAddress $ctx $ctx.nOffsetAddress += $member.size [void] $ctx.br.Read( $ctx.Buffers, 0, $member.size ) $fValue = 0.0 switch( $member.size ) { 4 { $fValue = $DISP_F32.Invoke( $ctx.Buffers, 0 ) $ctx.out.sw.WriteLine( $member.name + ': ' + $fValue ) } 8 { $fValue = $DISP_F64.Invoke( $ctx.Buffers, 0 ) $ctx.out.sw.WriteLine( $member.name + ': ' + $fValue ) } _ { Write-Error '引数が不正です。' -ErrorAction Stop } } } Function FC-DISP-BYTES( $ctx, $info, $members, $member ) { Display-OffsetAddress $ctx $ctx.nOffsetAddress += $member.size $ctx.AllocBuffer( $member.size ) [void] $ctx.br.Read( $ctx.Buffers, 0, $member.size ) $ctx.out.sw.Write( $member.name + ':' ) for( $i = 0; $i -lt $member.size; $i ++ ) { $ctx.out.sw.Write( ' ' + $ctx.Buffers[$i].ToString('X02') ) } $ctx.out.sw.WriteLine('') } Function FC-READ-BYTES( $ctx, $info, $members, $member ) { FC-DISP-BYTES $ctx $info $members $member $stream = [byte[]]::new( $member.size ) [Array]::Copy( $ctx.Buffers, $stream, $member.size ) $info[$member.pm.strMemberName] = $stream } Function Dump-X-V2-Main( $ctx, $param ) { $ctx.errorInfo.layer ++; for( $nMemberIndex = 0; $nMemberIndex -lt $param.members.Length; $nMemberIndex ++ ) { $ctx.errorInfo.memberIndex = $nMemberIndex + 1 $member = $param.members[$nMemberIndex] # TODO : デバッグ情報 # Write-Host ('!!! ' + $member.name) if( $member.fnInit -ne $Null ) { for( $i = 0; $i -lt $member.fnInit.Length; $i ++ ) { $ctx.errorInfo.initIndex = $i + 1 $fn = $member.fnInit[$i] & $fn $ctx $param.info $param.members $member } $ctx.errorInfo.initIndex = 0 } if( $member.subMember -eq $Null ) { if( $member.fn -eq $Null ) { FC-DISP-BYTES $ctx $param.info $param.members $member } else { & $member.fn $ctx $param.info $param.members $member } } else { $nLoopCounter = $member.loop if( $nLoopCounter -eq 0 ) { $nLoopCounter = 1 $fn = { Param($nLoop, $member) } } else { $fn = { Param($nLoop, $member) ("`t{0}[{1}]" -f $member.name ,$nLoop) } } $nBackupMember = $ctx.errorInfo.memberIndex $nBackupLoop = $ctx.errorInfo.loopIndex for( $i = 0; $i -lt $nLoopCounter; $i ++ ) { $ctx.errorInfo.loopIndex = $i + 1 & $fn ($i + 1) $member Dump-X-V2-Main $ctx $member.subMember } $ctx.errorInfo.loopIndex = $nBackupLoop $ctx.errorInfo.memberInde = $nBackupMember } } $ctx.errorInfo.memberIndex = 0 $ctx.errorInfo.layer --; } Function Dump-X-V2( $struct, $strInputPath, $strOutputPath ) { if( ($strInputPath -eq $Null) -band ($strOutputPath -eq $Null) ) { if( $struct -ne $Null ) { $strDescription = ''` + '書式 View-XXXX <入力ファイル> <出力ファイル>' + "`n"` + "`n"` + '入力ファイル ダンプするファイルのパス。' + "`n"` + '出力ファイル ダンプ結果を出力するファイルのパス。' + "`n"` + '' } else { $strDescription = ''` + '書式 Dump-X-V2 <ダンプ構造> <入力ファイル> <出力ファイル>' + "`n"` + "`n"` + 'ダンプ構造 ダンプ構造(連想配列)。(詳細は View-Dummy.ps1 を参照)' + "`n"` + '入力ファイル ダンプするファイルのパス。' + "`n"` + '出力ファイル ダンプ結果を出力するファイルのパス。' + "`n"` + '' } $strDescription += ''` + "`n"` + '注意 ダンプするファイルがダンプ構造よりも小さいサイズの場合、例外が発生する。' + "`n"` + '' Write-Output $strDescription return } $ctx = [DumpX2Context]::new() try { $ctx.AllocBuffer( 4096 ) Dump-X-V2-Setup $ctx $strInputPath $strOutputPath $ctx.struct = $struct } catch { Throw } try { Dump-X-V2-Main $ctx $struct } catch { $strFormat = "`n"` + ' layer: {0}' + "`n"` + ' member: {0}' + "`n"` + ' loop: {0}' + "`n"` + ' init: {0}' + "`n"` + '' $strMessage = $strFormat -f` $ctx.errorInfo.layer,` $ctx.errorInfo.memberIndex,` $ctx.errorInfo.loopIndex,` $ctx.errorInfo.initIndex Write-Error ('Dump-X-V2の実行中にエラーが発生しました。' + $strMessage) Throw } finally { Dump-X-V2-Cleanup $ctx } }