X作成講座 on TC(5) リソースを扱う


 さて、現在のスタックにインストールされているサウンドのリストを返すXです。
 リソースのリストを得る場合の基本的な流れは

  まずそのリソースの総数を得る
  1番目から順にループ
   n番目のリソースを読む
   そのリソースの名前を取り出す
   ハイパカに返す文字列にその名前を繋げる
  ループ終わり
  ハイパカに返す

 となります。
 リソースはIDや名前で指定する他に「○番目」というアクセスが出来るようになっているので、それを利用するわけです。

#include "HyperXCmd.h"
#include "SetUpA4.h"

/* Prototypes */
void myRoutin( XCmdPtr paramPtr );
void getSoundList( XCmdPtr paramPtr );
void pStrCat( Str255 str1, Str255 str2 );

/* Main */
pascal void  main( XCmdPtr paramPtr ){
	RememberA0( );
	SetUpA4( );
	getSoundList( paramPtr );
	RestoreA4( );
}

/* my routin */
void getSoundList( XCmdPtr paramPtr ){
	Str255		theStr, resName, resultStr;
	Handle		resHand;
	short		theID, itemNum, i;
	ResType		theType;

	resultStr[0] = 0;
	itemNum = Count1Resources('snd ');
	SetResLoad( false );
	for ( i = 1; i <= itemNum; i++ ){
		resHand = Get1IndResource( 'snd ', i );
		GetResInfo( resHand, &theID, &theType, resName );
		pStrCat( resultStr, resName );
		pStrCat( resultStr, "\p\r" );
	}
	SetResLoad( true );

	paramPtr->returnValue = PasToZero( paramPtr, resultStr );
}

/* connect str2 to str1 */
void pStrCat( Str255 str1, Str255 str2 ){
	short		lgth;

	lgth = str2[0];
	if ( str1[0] + lgth > 255 )    lgth = 255 - str1[0];
	BlockMove( str2 + 1, str1 + 1 + str1[0], lgth );
	str1[0] += lgth;
}

 リソースを読むときは「読めるもの全て」なのか「現在のファイルだけ」なのかを考えないとなりません。GetResource のようなルーチンはホーム、ハイパカ、システムまでのリソースを全て読みますが、Get1Resource のような "1" の付くルーチンは「現在のファイル」(=Xならカレントスタック)だけを対象とします。このソースでは全て "1" の付くルーチンを使っているので、カレントスタックのサウンドリソースだけをリストアップします。

 Count1Resources でカレントファイルに含まれる "snd " リソースの数を数え、Get1IndResource で順にリソースハンドルを得ます。GetResInfo にそのハンドルを指定すると変数 resName にリソース名が返ってくるので、それを pStrCat ルーチンを利用してリストの形にしています。

 ループの前後に SetResLoad がありますが、ここに false を渡すとその後のリソースルーチンで「リソース情報は得るがリソースそのものはメモリに読み込まない」という状態になります。ここではリソース名だけが欲しいのでループに入る前に false を指定して、その後元に戻しています。(戻すのを忘れずに!!)
 まぁ実際にはこれはやらなくても大丈夫ですが、実行しておくと無駄なディスクアクセスや無用なメモリ移動を防ぐことが出来ます。

 pStrCat ルーチンはP文字列をつなげるユーティリティです。P文字列では str[0] がその文字列の長さであり、文字列そのものは str + 1 というアドレスから始まっていることを思い出して下さい。頑張って読み解くのもいいし、そのまんまイタダキしてしまうんでも構いません。
 C言語で文字列を扱うとパズルのようになりがちですが、このような汎用ルーチンを作っておくと使い回しが出来て便利です。(ちなみにこのルーチンは結合した結果の文字列が255文字を越える場合は越える分の文字を捨てています) 呼び出しているルーチン側で resultStr[0] = 0; を実行していることに注意。これを忘れるとゴミが入り込みます。

   −・−・−・−・−・−・−・−・−・−・−・−・−・−・−・−

 次にカレントスタックだけではなく、現在利用できるサウンドリソースの全てのリソース名を得るXを作ります。 "1" の付かないリソースルーチンは、カレントスタック、ホーム、ハイパカ、システムの他、StackUsing してあるスタックのリソースまでを読みます。これら以外のファイル(やスタック)のリソースを読むにはファイルパスを指定してリソースフォークを開かなければなりませんが、Xでこれをやるには少々面倒な手続きが必要なので、取りあえず割愛します。


#include "HyperXCmd.h"
#include "SetUpA4.h"

/* Prototypes */
void myRoutin( XCmdPtr paramPtr );
void getAllSoundList( XCmdPtr paramPtr );
Boolean putStrAfterC( Handle resultHand, long *resultOfs, Str255 theStr );
/* Main */
pascal void  main( XCmdPtr paramPtr ){
	RememberA0( );
	SetUpA4( );
	getAllSoundList( paramPtr );
	RestoreA4( );
}

/* my routin */
void getAllSoundList( XCmdPtr paramPtr ){
	Str255		theStr, resName, resultStr;
	Handle		resultHand, resHand;
	long		resultOfs;
	short		theID, itemNum, i;
	ResType		theType;

	resultOfs = 0;
	resultHand = NewHandle( 0 );
	itemNum = CountResources('snd ');
	SetResLoad( false );
	for ( i = 1; i <= itemNum; i++ ){
		resHand = GetIndResource( 'snd ', i );
		GetResInfo( resHand, &theID, &theType, resName );
		if ( resName[0] != 0 ){
			if ( putStrAfterC( resultHand, &resultOfs, resName ) == false ){
				SetResLoad( true );
				DisosHandle( resultHand );
				paramPtr->returnValue = PasToZero( paramPtr, "\pMemErr" );
				return;
			}
			if ( putStrAfterC( resultHand, &resultOfs, "\p\r" ) == false ){
				SetResLoad( true );
				DisosHandle( resultHand );
				paramPtr->returnValue = PasToZero( paramPtr, "\pMemErr" );
				return;
			}
		}
	}
	SetResLoad( true );

	ZeroTermHandle( paramPtr, resultHand );
	paramPtr->returnValue = resultHand;
}

/* -- put P-string after C string Handle -- */
Boolean putStrAfterC( Handle resultHand, long *resultOfs, Str255 theStr ){
	SetHandleSize( resultHand, *resultOfs + theStr[0] );
	if ( MemError() )	return false;
	BlockMove( theStr +1, *resultHand + *resultOfs, theStr[0] );
	*resultOfs += theStr[0];
	return true;
}

 基本的な流れは一緒ですが、Count1Resources ではなく CountResources、Get1IndResource ではなく GetIndResource を使っています。システムには名前の付いていないサウンドリソースもあったので、resName の文字長が 0 でない場合のみリスト処理をするようにしました。

 返値は255文字では心許ないので、直接C文字列ハンドルに繋げていくようにしています。念のためハンドルを拡大出来なかった場合の処理も加えているのでソースはやや複雑になりましたが、それほど難しいことはしていません。

 putStrAfterC ルーチンは文字列ハンドルを拡大しながらP文字列を繋げていくルーチンです。第3引数に渡したP文字列を第1引数のC文字列ハンドルの後ろにくっつけますが、その時ハンドルの拡大処理までをやります。
 第2引数に変数 resultOfs のアドレス(&)を渡しています。resultOfs は「次に文字列を書き込むオフセット」を収めている変数ですが、これをそのまま引数として渡すのではなく、アドレスで渡しているのがミソです。こうすることでサブルーチン内でメインルーチンの変数の値を書き換えることが出来るようになります。このルーチンを呼んだあとは、変数 resultOfs は新しい「次に文字列を書き込むオフセット」に書き換えられています。

 最後に paramPtr->returnValue にハンドルを書き込む前に、グルールーチンの ZeroTermHandle を呼んでいます。これは第2引数に渡したハンドルを1バイト拡大して0を書き込むルーチンです。C文字列の終端処理ですね。これをやっておかないと、返値の文字列の最後にゴミがついたりします。


Next



THINK C Lab.

UDI's HomePage
inserted by FC2 system