View Issue Details

Category
整備班:YAYA
SeverityminorReproducibilityalways 
Status new 
Summary0000760: FREADBINで、ファイル終端に達したときに-1が返されていない?
DescriptionYAYA Tc573-1にて確認。ゴーストは「それはあなたです!」
設定ファイルの文字コード判定のためにFREADBIN関数で対象ファイルの読み取りを行っていますが、ファイル終端になっても-1が返されていないようです。

以下のようなコードでファイル終端が来たらループを抜けるようにしています。
本来ならファイル終端が来たときに-1が返されてループから脱出するはずなのに、そのまま次のバイトを読み込もうとしてエラーが発生→空文字列が返る→2つ目の条件で脱出、と言う流れになっています。
このことから「ファイル終端に達しても-1が返っていないのではないか?」と推測しました。
お忙しい中恐縮ですが、調査お願いします。

コード(行頭のインデント省略)
_s1 = FREADBIN(_path,1);
if (_s1 == -1) { break; } //ファイル終端に達したらブレイク
if (_s1 == '') { break; } //正常に読み込めないときはブレイク

念のため、この処理を含む辞書ファイルを添付しておきます(読み込み範囲等はデバッグ目的で大きな数値を設定しています)。

TAMAでは以下のような警告が出ています。
(ファイルパス省略)yaya_library.dic(791) : warning W0013 : 処理に失敗しました. : FREADBIN

OSおよびSSPのバージョンは以下の通りです。
SPDebugger/2.17.24106.A
Windows NT 10.0.26200
UAC: Enabled,Limited
Time: 2026/3/31 10:36:11.583
Phys.Mem: 7139/16230MB PageFile: 7855/18406MB
CPU : Intel 0.6.14.6 1800MHz Features:MMX SSE HT AES-NI (Intel(R) Core(TM) i5-8265U CPU @ 1.60GHz)
Package:1 Node:1 Core:4 Thread:8
SSP/2.7.91 (20260329-6; Windows NT 10.0.26200)
Attached Files
yaya_library.dic (44,104 bytes)   
//======================================================================================
//	特定のゴーストに依存せず、使い回せる関数を共通ライブラリ化。
//======================================================================================
//======================================================================================
//	起動と終了・変数初期化
//======================================================================================
OnInitialize
{	//------------------------------------------------------------------------------
	//	ゴーストが読み込まれたときの処理
	//------------------------------------------------------------------------------

	//リロード判定
	RELOAD = 0;
	if (reference[0] == 'reload') { RELOAD = 1; }

	//設定初期化
	CallFunction('ValueInitialize');

	//ランダムトーク用配列の初期化
	Talk_Array = IARRAY;
	TalkCount  = 0;

	//マウス操作に関する反応用変数を初期化
	PreviousArea_Move	= '';	//撫で反応
	PreviousArea_Wheel	= '';	//ホイール反応
	PreviousTime_Move	= 0;
	PreviousTime_Wheel	= 0;
	Stroke_Move		= 0;
	Stroke_Wheel		= 0;
	マウス反応中		= 0;

	//ランダムトークをファイルから読み込み
	Talk_Save_File  = 'randomtalk.sav';	//ファイル書き出しでも使うのでグローバル変数に
	_Filename_OLD   = 'randomtalk_utf.sav';	//UTF-8専用のファイル名
	if (FATTRIB(_Filename_OLD) != -1) {
		if (FATTRIB(Talk_Save_File) == -1) {
			//UTF-8専用ファイルが残っていて、セーブファイルがないときはファイル名を変更。
			FRENAME(_Filename_OLD,Talk_Save_File);
		}
	}

	FCHARSET(CheckFileCharset(Talk_Save_File));
	if (FOPEN(Talk_Save_File,'r')) {
		while 1 {
			_talk = FREAD(Talk_Save_File);
			if (_talk == -1) {
				break;
			}
			Talk_Array ,= _talk;
		}
		FCLOSE(Talk_Save_File);
	}
}

OnDestroy
{	//------------------------------------------------------------------------------
	//	ゴースト終了時の処理
	//------------------------------------------------------------------------------

	//リロード判定
	RELOAD = 0;
	if (reference[0] == 'reload') { RELOAD = 1; }

	//不要変数の削除等
	CallFunction('ValueClear');

	Talk_Array = ASORT('string',Talk_Array);
	//ランダムトークをファイルに書き出し
	FCHARSET('UTF-8');	//UTF-8
	if (ARRAYSIZE(Talk_Array)) {
		if (FOPEN(Talk_Save_File,'w')) {
			foreach Talk_Array ; _string {
				if (_string != '') {
					FWRITE(Talk_Save_File,_string);
				}
			}
			FCLOSE(Talk_Save_File);
		}
	}

	if (1) {
		//保存不要な変数を削除し、セーブファイルの肥大化を防ぐ
		ERASEVAR('Talk_Save_File');
		ERASEVAR('PreviousArea_Move');
		ERASEVAR('PreviousArea_Wheel');
		ERASEVAR('PreviousTime_Move');
		ERASEVAR('PreviousTime_Wheel');
		ERASEVAR('RELOAD');
		ERASEVAR('RANDOM_TALK_COUNT');
		ERASEVAR('Script_Yoyaku');
		ERASEVAR('Stroke_Move');
		ERASEVAR('Stroke_Wheel');
		ERASEVAR('Talk_Array');
		ERASEVAR('TalkCount');
		ERASEVAR('TimeZoneDiff');
		ERASEVAR('マウス反応中');
	}
}

//======================================================================================
//	ランダムトーク関連(ランダムトーク、トランスレート)
//======================================================================================
RandomTalkMain
{	//------------------------------------------------------------------------------
	//	ランダムトーク。汎用リストを利用し、トークが重複しないようにする
	//------------------------------------------------------------------------------
	_RandomTalk_Length = STRLEN(JOIN(RandomTalkArray,''));
	if (!ISVAR('RANDOM_TALK_LENGTH')) { RANDOM_TALK_LENGTH = _RandomTalk_Length; }

	if (RANDOM_TALK_LENGTH != _RandomTalk_Length) {
		//トークが変更されたらリストを再作成
		//総件数ではなく文字列長を見ることで、トーク件数が変わらず、内容だけ変更した場合にも対応。
		//文字列長を見るため、トークリストでは変数等の置き換えが発生しないようにする(""を使わない、ANYを使わない)こと。
		Talk_Array = RandomTalkArray;
		RANDOM_TALK_LENGTH = _RandomTalk_Length;
	} elseif (ARRAYSIZE(Talk_Array) == 0) {
		//リストが空っぽ(未定義含む)の場合、リスト再作成
		Talk_Array = RandomTalkArray;
	}

	ANY(Talk_Array);		//ランダムトークを格納した配列から要素を1つ取り出す
	Talk_Array[LSO] = IARRAY;	//取り出した要素を配列から削除して、配列を作成し直すまで重複が発生しないようにする
}

Translate_Main
{	//------------------------------------------------------------------------------
	//	トランスレート(自動ウェイト、スクリプト整形)
	//------------------------------------------------------------------------------
	_script_base = JOIN(_argv,',');		//引数を結合
	_script = _script_base[0,'\e'];		//スクリプト部と
	_command = _script_base[1,'\e'];	//\eの後ろに書かれた各種コマンド部を分離

	//定型句置換
	_script = REPLACE(_script,'\_h','\n[150]');
	_script = REPLACE(_script,'\n[half]\n','\n[150]');
	_script = REPLACE(_script,'\n\n[half]','\n[150]');
	_script = REPLACE(_script,'\n\n','\n[200]');
	_script = REPLACE(_script,'\h','\0');
	_script = REPLACE(_script,'\u','\1');
	_script = REPLACE(_script,'‥','…');

	//余計な変換をしないように特定の文字を置換。置換対象のCHR(1)とCHR(2)はstring.dicで使用実績があるので利用。
	_script = REPLACE(_script,'\!',"%(CHR(1))%(CHR(2))");
	_script = REPLACE(_script,'!stayontop',"%(CHR(1))");
	_script = RE_REPLACEEX(_script,'(http|https|ftp)://(.+)\?(.+)',"$1://$2%(CHR(2))$3");

	//全角・半角変換。英数字は半角に、カナや句読点は全角に
	_script = ZEN2HAN(_script,'number,alphabet');
	_script = HAN2ZEN(_script,'kana');

	//疑問符・感嘆符は全角に。
	_script = REPLACE(_script,'?','?');
	_script = REPLACE(_script,'!','!');

	//Yahoo!に関しては、感嘆符を半角に
	_script = REPLACE(_script,'Yahoo!','Yahoo!');

	//置換したものを戻す。
	_script = REPLACE(_script,"%(CHR(1))%(CHR(2))",'\!');
	_script = REPLACE(_script,"%(CHR(1))",'!stayontop');
	_script = REPLACE(_script,"%(CHR(2))",'?');

	//重複対策
	_script =    REPLACE(   _script,'シェルシェル','シェル');
	_script =    REPLACE(   _script,'…。','…');
	_script = RE_REPLACE(   _script,'…+','……');	//三点リーダーの数を揃える
	_script = RE_REPLACE(   _script,'(、|。)」','」');
	_script = RE_REPLACEEX( _script,'(先生|さん|たん|くん|様|ちゃん)さん','$1');
	_script = RE_REPLACEEX( _script,'(、|。)+','$1');
	_script = RE_REPLACEEX( _script,'(!|?|…)(、|。)','$1');

	//------------------------------------------------------------------------------
	//	自動ウェイト処理・ここから
	//------------------------------------------------------------------------------
	//余計な変換をしないように特定の文字を置換。置換対象のCHR(1)とCHR(2)はstring.dicで使用実績があるので利用。
	_script = RE_REPLACEEX( _script,'(、|。|!|?|…)\n','$1@@\n');	//句読点などと改行が連続しているとき、重複してウェイトを入れないようにエスケープ文字を挿入
	_script =    REPLACE(   _script,'@@\',"%(CHR(1))");		//自動ウェイト対策。\の前に@@を入れると変換。
	_script =    REPLACE(   _script,'@@' ,'');			//それ以外の@@は消去。
	_script =    REPLACE(   _script,'\x\',"%(CHR(1))%(CHR(2))");	//ユーザークリック待ちの場合はその後のウェイトを入れない

	_script = RE_REPLACE(_script,'\\w\d','');			//いったんウェイト削除(\w1~\w9のみ。\_w[XXX]形式のものは残す

	_wait_short	= 150;	//短いウェイト
	_wait_middle	= 300;	//中間のウェイト
	_wait_long	= 600;	//長いウェイト

	//さくらスクリプトタグでスクリプトを分解
	//この辺は、AYAYA03のTips→OnTranslateの使い方、を参考にしている。
	// https://emily.shillest.net/ayaya/?cmd=read&page=Tips%2FOnTranslate%E3%81%AE%E4%BD%BF%E3%81%84%E6%96%B9&word=%E3%82%BF%E3%82%B0%20%E6%AD%A3%E8%A6%8F%E8%A1%A8%E7%8F%BE
	_SCRIPT_ARRAY = RE_SPLIT(_script,'\\(\\|q\[.*?\]\[.*?\]|[!&8cfijmpqsn]\[.*?\]|[-*+014567bcehntuvxz]|_[ablmsuvw]\[.*?\]|__(t|[qw]\[.*?\])|_[!?+nqsV]|[sipw][0-9])');
	_SCRIPT_NUM   = ARRAYSIZE(_SCRIPT_ARRAY);	//要素数を取得
	_TAG_ARRAY    = RE_GETSTR;			//タグを抽出
	_QuickSection = 0;				//クイックセクションフラグ
	_script_temp  = '';

	for _i = 0 ; _i < _SCRIPT_NUM ; _i++ {
		_script_temp2	 = _SCRIPT_ARRAY[_i];	//スクリプト本体を取得
		_tag		 = _TAG_ARRAY[_i];	//タグを取得
		_script_temp2	+= _tag;		//スクリプトにタグを追加

		if (!_QuickSection) {
			//クイックセクション内でなければ、設定に従ってウェイト挿入。
			_script_temp2 = RE_REPLACEEX( _script_temp2,'(、|……)',"$1\_w[%(_wait_short)]");		//、と三点リーダーの「あと」は短めのウェイトを挿入
			_script_temp2 = RE_REPLACEEX( _script_temp2,'(。|?|!)',"$1\_w[%(_wait_middle)]");		//。や!?の「あと」は中程度のウェイトを挿入。
			_script_temp2 = RE_REPLACEEX( _script_temp2,'(\\0|\\1|\\n|\\s)',"\_w[%(_wait_middle)]$1");	//スコープやサーフェスの切り替え、改行の「前」は中程度のウェイトを挿入
			_script_temp2 =    REPLACE(   _script_temp2,'\c',"\_w[%(_wait_long)]\c");			//\cタグの「前」は長めのウェイトを追加
		}
		if ('\_q' _in_ _tag) { _QuickSection = !_QuickSection; }						//クイックセクションの判定。\_qタグが出るごとにフラグを反転
		_script_temp += _script_temp2;
	}
	_script = _script_temp;

	//ウェイト正規化
	_script = RE_REPLACE(  _script,'^(\\_w\[\d+\])+','');					//スクリプト先頭のウェイトを除去
	_script = RE_REPLACE(  _script, '(\\_w\[\d+\])+,On',',On');				//スクリプト内で関数を呼び出す部分に余分に付いたウェイトを除去
	_script = RE_REPLACE(  _script, '(\\_w\[\d+\])+$','');					//スクリプト末尾のウェイトを除去
	_script = RE_REPLACEEX(_script, '(\\_w\[\d+\])+(\\0|\\1)(\\_w\[\d+\])\\c','$3$2\c');	//\cタグ前後のウェイトを調整
	_script = RE_REPLACEEX(_script, '(\\_w\[\d+\])+?(」|\))', '$2$1');			//閉じカッコ前のウェイトをカッコの後ろへ
	_script = RE_REPLACEEX(_script, '(\\_w\[\d+\])(\\_w\[\d+\]|\\!|!|?)','$2');		//二重ウェイト、特殊タグ、!?前のウェイトを除去
	_script = RE_REPLACEEX(_script, '(\\s\[\-?\d+\]|\\t|\\0|\\1|\\n|\\n\[half\]|\\n\[\d+\]|\\c|\\b\[\-?\d+\]|\\q\[.+?\]|\\_q)(\\_w\[\d+\])+','$1');	//さくらスクリプトタグの直後にくっついてるウェイトを除去

	//置換したものを戻す。
	_script = REPLACE(_script,"%(CHR(1))%(CHR(2))",'\x\');
	_script = REPLACE(_script,"%(CHR(1))",'\');	//自動ウェイト対策。エスケープ文字を\に戻す。

	//任意で挿入したウェイトを置換
	_script = REPLACE(_script,'###WAIT_LONG###',"\_w[%(_wait_long)]");
	_script = REPLACE(_script,'###WAIT_MIDDLE###',"\_w[%(_wait_middle)]");
	_script = REPLACE(_script,'###WAIT_SHORT###',"\_w[%(_wait_short)]");

	//------------------------------------------------------------------------------
	//	自動ウェイト処理・ここまで
	//------------------------------------------------------------------------------
	_script = "%(_script)\e%(_command)";
	_script;
}

SetAiTalkInterval
{	//------------------------------------------------------------------------------
	//	AIトークの間隔を30秒~5分の間で、ランダムに変更する。
	//------------------------------------------------------------------------------
	aitalkinterval = RAND(270) + 30;
}

OnAiTalk
{	//------------------------------------------------------------------------------
	//	ランダムトークを発動させる
	//------------------------------------------------------------------------------
	TalkCount++;	//トークカウンタをインクリメント

	//トーク間隔がランダムになっている場合、トーク間隔を再設定
	if (TalkInterval_Random) {
		SetAiTalkInterval;
	}

	//通常のランダムトーク、ただしチェイン中はチェイントーク
	if (CHAIN.IDName == '') {
		//変数の展開。CHR(0x22)="
		_script = EVAL(CHR(0x22)+RandomTalkSub+CHR(0x22));
		_script;
	} else {
		CallFunction('ChainTalk');
	}
}

//======================================================================================
//	入力デバイス関連(マウスやキーボード入力への反応)
//======================================================================================
OnMouseMove
{	//------------------------------------------------------------------------------
	//	撫で反応
	//------------------------------------------------------------------------------
	MouseCounter('Move');
}

OnMouseWheel
{	//------------------------------------------------------------------------------
	//	ホイール反応
	//------------------------------------------------------------------------------
	MouseCounter('Wheel');
}

MouseCounter
{	//------------------------------------------------------------------------------
	//	マウス操作の捕捉。引数に'Wheel'を指定するとホイール反応
	//------------------------------------------------------------------------------
	_PreviousArea	= PreviousArea_Move;	//直前のエリア
	_PreviousTime	= PreviousTime_Move;	//直前の時刻
	_Stroke		= Stroke_Move;		//ストローク数
	_Threshold	= 64;			//反応しきい値

	_TYPE  = _argv[0];	//ホイールかそれ以外か
	_Scope = reference[3];	//マウスイベントが発生しているスコープ
	_Area  = reference[4];	//マウスイベントが発生しているエリア

	if (_TYPE == 'Wheel') {
		//ホイール操作に対する反応
		_PreviousArea	= PreviousArea_Wheel;	//直前のエリア
		_PreviousTime	= PreviousTime_Wheel;	//直前の時刻
		_Stroke		= Stroke_Wheel;		//ストローク数
		_Threshold	= 2;			//反応しきい値
	}

	if ((_Area != '')||(_Scope == 1)) {
		//どこかが撫でられている
		if (_Area == _PreviousArea) {
			_Interval = systemuptime - _PreviousTime;
			if (_Interval > 1) {
				//1秒以上間隔が空いたらカウンタをリセット
				_Stroke = 0;マウス反応中 = 0;
			}

			//現在時刻を取得
			_PreviousTime = systemuptime;
			_Stroke++;

			//触られた量が閾値を超えたら「触られている」と判断
			if (_Stroke > _Threshold) {
				//触られた
				if (!マウス反応中) {
					//既に反応中でなければ、触られた部位を見てトークする
					マウス反応中 = 1;
					'\t' + CallFunction('MouseReaction',_TYPE);
					_Stroke = 0;マウス反応中 = 0;
				}
			}
		} else {
			_Stroke = 0;マウス反応中 = 0;
		}
		_PreviousArea = _Area;
	} else {
		// 定義された部位はどこも撫でられていない
		_Stroke = 0;マウス反応中 = 0;
	}

	case _TYPE {
		//マウスアクションに応じて適切な変数に値を代入する
		when 'Wheel' {
			PreviousArea_Wheel = _PreviousArea;
			PreviousTime_Wheel = _PreviousTime;
			Stroke_Wheel 	   = _Stroke;
		}

		others {
			PreviousArea_Move = _PreviousArea;
			PreviousTime_Move = _PreviousTime;
			Stroke_Move 	  = _Stroke;
		}
	}
}

OnKeyPress
{	//------------------------------------------------------------------------------
	//	キー入力処理
	//------------------------------------------------------------------------------
	if (DEBUGMODE) {
		Debug_Key(reference[0]);
	 } elseif ((reference[0] == 't')||(reference[0] == 'f12')) {
	 	 OnAiTalk;
	 }
}

//======================================================================================
//	日付・時刻関連(祝日取得、年月日等を数値として取得、日数計算、等)
//======================================================================================
祝日の名称
{	//------------------------------------------------------------------------------
	//	祝日の名称を取得する。引数はずらす日数。省略(0)で当日。
	//------------------------------------------------------------------------------
	_Year_Month_Day	= Year_Month_Day;
	_Month_Day	= Month_Day;

	if (_argc > 0) {
		_TIME_ARRAY	= DateTimeShift(2,TOINT(_argv[0]));
		_Year_Month_Day	= _TIME_ARRAY[0] * 100 * 100 + _TIME_ARRAY[1] * 100 + _TIME_ARRAY[2];
		_Month_Day	= _TIME_ARRAY[1] * 100 + _TIME_ARRAY[2];
	}

	//祝日の名称は、祝日リストのものと一致している必要がある。入力ミス等に注意。
	case _Month_Day {
		//日付が固定された祝日
		when 101  { '元日';	    }
		when 211  { '建国記念の日'; }
		when 223  { '天皇誕生日';   }
		when 429  { '昭和の日';	    }
		when 503  { '憲法記念日';   }
		when 504  { 'みどりの日';   }
		when 505  { 'こどもの日';   }
		when 1103 { '文化の日';	    }
		when 1123 { '勤労感謝の日'; }
		others { //年によって日付が変動する祝日
			case _Year_Month_Day {
			//2026年
				when 20260112 { '成人の日';	}
				when 20260320 { '春分の日';	}
				when 20260506 { '振替休日';	}
				when 20260720 { '海の日';	}
				when 20260811 { '山の日';	}
				when 20260921 { '敬老の日';	}
				when 20260922 { '国民の休日';	}
				when 20260923 { '秋分の日';	}
				when 20261012 { 'スポーツの日';	}
			//2027年
				when 20270111 { '成人の日';	}
				when 20270321 { '春分の日';	}
				when 20270322 { '振替休日';	}
				when 20270719 { '海の日';	}
				when 20270811 { '山の日';	}
				when 20270920 { '敬老の日';	}
				when 20270923 { '秋分の日';	}
				when 20271011 { 'スポーツの日';	}
			}
		}
	}
}

祝日の説明
{	//------------------------------------------------------------------------------
	//	祝日名を検索キーとして、祝日の説明を取得する。
	//------------------------------------------------------------------------------
	if (_argv[0] != '') { _index = RE_ASEARCH(".*%(_argv[0]).*",祝日リスト);祝日リスト[_index][2]; }
}

振替休日の取得
{	//------------------------------------------------------------------------------
	//	振替休日が、何の祝日に対する振替休日なのか取得する
	//------------------------------------------------------------------------------
	_i = 0;while (DateTimeShift(2,_i)[3] != 0) { _i--; }	//直前の日曜日(振替休日になる条件)を探す
	"%(祝日の名称(_i))の振替休日";				//直前の日曜日に当たる祝日名を取得。
}

祝日リスト : array
{	//------------------------------------------------------------------------------
	//	祝日のリスト。祝日名とその説明。説明文は内閣府の'「国民の祝日」について'より。
	//	https://www8.cao.go.jp/chosei/shukujitsu/gaiyou.html
	//	※項目最初の数字はソート用のキー。実際の日付とは異なるものもある。
	//------------------------------------------------------------------------------
	'0101,元日,年のはじめを祝う。';
	'0115,成人の日,おとなになったことを自覚し、みずから生き抜こうとする青年を祝いはげます。';
	'0211,建国記念の日,建国をしのび、国を愛する心を養う。';
	'0223,天皇誕生日,天皇の誕生日を祝う。';
	'0320,春分の日,自然をたたえ、生物をいつくしむ。';
	'0429,昭和の日,激動の日々を経て、復興を遂げた昭和の時代を顧み、国の将来に思いをいたす。';
	'0503,憲法記念日,日本国憲法の施行を記念し、国の成長を期する。';
	'0504,みどりの日,自然に親しむとともにその恩恵に感謝し、豊かな心をはぐくむ。';
	'0505,こどもの日,こどもの人格を重んじ、こどもの幸福をはかるとともに、母に感謝する。';
	'0702,海の日,海の恩恵に感謝するとともに、海洋国日本の繁栄を願う。';
	'0802,山の日,山に親しむ機会を得て、山の恩恵に感謝する。';
	'0915,敬老の日,多年にわたり社会につくしてきた老人を敬愛し、長寿を祝う。';
	'0920,秋分の日,祖先をうやまい、なくなった人々をしのぶ。';
	'1010,スポーツの日,スポーツを楽しみ、他者を尊重する精神を培うとともに、健康で活力ある社会の実現を願う。';
	'1103,文化の日,自由と平和を愛し、文化をすすめる。';
	'1123,勤労感謝の日,勤労をたっとび、生産を祝い、国民たがいに感謝しあう。';
	'9998,振替休日,祝日法第3条第2項による休日';
	'9999,国民の休日,祝日法第3条第3項による休日'
}

連休判定
{	//------------------------------------------------------------------------------
	//	連休日数を数える。
	//	引数:シフト日数
	//	戻り値:(連休の日数),(土曜日と被る祝日があれば1,なければ0),(土曜日と被った祝日)
	//------------------------------------------------------------------------------
	_連休日数 = 0;
	_土曜被り = 0;
	_被り祝日 = '';
	_シフト = TOINT(_argv[0]);
	for _i = _シフト ; _i < _シフト + 366 ; _i++ {
		_曜日 = DateTimeShift(2,_i)[3];
		_祝日 = 祝日の名称(_i);
		//土曜日と祝日が重なっているか?
		if ((_祝日 != '')&&(_曜日 == 6)) { _土曜被り = 1;_被り祝日 = _祝日; }
		//祝日でもなく、土曜でも日曜でもない=平日
		if ((_祝日 == '')&&(_曜日 != 6)&&(_曜日 != 0)) { break; }
		_連休日数++;
	}
	(_連休日数,_土曜被り,_被り祝日);
}

時間帯の取得
{	//------------------------------------------------------------------------------
	//	朝/昼/夜などをおおざっぱに調べる
	//------------------------------------------------------------------------------
	case hour {
		when 4-5   { '早朝'; }
		when 6-10  { '朝';   }
		when 11-13 { '昼';   }
		when 14-15 { '午後'; }
		when 16-17 { '夕方'; }
		when 18-22 { '夜';   }
		others     { '深夜'; }
	}
}

挨拶_標準
{	//------------------------------------------------------------------------------
	//	時間帯に合わせて標準的な挨拶を返す
	//------------------------------------------------------------------------------
	case 時間帯の取得 {
		when '早朝'	{ 'おはようございます。'; }
		when '朝'	{ 'おはようございます。'; }
		when '昼'	{ 'こんにちは。';	  }
		when '午後'	{ 'こんにちは。';	  }
		when '夕方'	{ 'こんにちは。';	  }
		when '夜'	{ 'こんばんは。';	  }
		when '深夜'	{ 'こんばんは。';	  }
		others		{ 'こんにちは。';	  }
	}
}

季節の取得
{	//------------------------------------------------------------------------------
	//	季節をおおざっぱに調べる
	//------------------------------------------------------------------------------
	case month {
		when 3-5  { '春'; }
		when 6-8  { '夏'; }
		when 9-11 { '秋'; }
		others	  { '冬'; }
	}
}

曜日の取得
{	//------------------------------------------------------------------------------
	//	引数に応じて曜日を返す
	//------------------------------------------------------------------------------
	('日,月,火,水,木,金,土')[TOINT(_argv[0])];
}

DateTimeShift
{	//------------------------------------------------------------------------------
	//	現在時刻から、
	//	第1引数で指定されたもの(GETTIME関数の戻り値の序数)を
	//	第2引数で指定された数値分だけシフトする。
	//------------------------------------------------------------------------------
	_TIME_ARRAY = GETTIME;
	_TIME_ARRAY[TOINT(_argv[0])] += TOINT(_argv[1]);
	GETTIME(GETSECCOUNT(_TIME_ARRAY));
}

Year_Month_Day
{	//------------------------------------------------------------------------------
	//	年月日を数値で返す(例:2004/09/06→20040906)
	//------------------------------------------------------------------------------
	year * 10000 + month * 100 + day;
}

Year_Month
{	//------------------------------------------------------------------------------
	//	年月を数値で返す(例:2004/09/06→200409)
	//------------------------------------------------------------------------------
	year * 100 + month;
}

Month_Day
{	//------------------------------------------------------------------------------
	//	月日を数値で返す(例:2004/09/06→906)
	//------------------------------------------------------------------------------
	month * 100 + day;
}

Hour_Min_Sec
{	//------------------------------------------------------------------------------
	//	時分秒を数値で返す(例:11時22分33秒→112233)
	//------------------------------------------------------------------------------
	hour * 10000 + minute * 100 + second;
}

Hour_Min
{	//------------------------------------------------------------------------------
	//	時分を数値で返す(例:11時22分33秒→1122)
	//------------------------------------------------------------------------------
	hour * 100 + minute;
}

Min_Sec
{	//------------------------------------------------------------------------------
	//	分秒を数値で返す(例:11時22分33秒→2233)
	//------------------------------------------------------------------------------
	minute * 100 + second;
}

GetInterval
{	//------------------------------------------------------------------------------
	//	現在時刻と、引数で渡された時刻の差を返す。
	//	引  数:GETTIME関数で取得される日時の配列
	//	戻り値:差分(秒数)、差分(日数)、GETTIME関数で取得される日時の配列
	//		※GETTIME関数で取得される日時の配列の番号が2つズレる
	//------------------------------------------------------------------------------
	_Interval = GETSECCOUNT - GETSECCOUNT(_argv);
	if (_Interval < 0) {
		//インターバルがマイナスの場合、とりあえず0にする。
		_Interval = 0;
	}

	//時差を補正しつつ差分を取得
	_TIME_ARRAY = GETTIME(_Interval + TimeZoneDiff);

	//日数を取得。86400=60*60*24(1日の秒数)。
	_DateDiff = TOINT(_Interval / 86400);

	//戻り値配列を構築
	_Value  = IARRAY;
	_Value ,= _Interval;	//[0]
	_Value ,= _DateDiff;	//[1]
	_Value ,= _TIME_ARRAY;	//[2]~
	_Value;
}

DateTimeCalc
{	//------------------------------------------------------------------------------
	//	日数と時間の計算。年月日・時分秒を配列で渡し、比較した結果を返す。
	//------------------------------------------------------------------------------
	_base_date = _argv[0];	//比較元
	_trgt_date = _argv[1];	//比較先
	_TIME_DIFF = 0;

	_DATE_DIFF = J_DATE(_trgt_date[0],_trgt_date[1],_trgt_date[2]) - J_DATE(_base_date[0],_base_date[1],_base_date[2]);

	_trgt_time = TOINT(_trgt_date[3]) * 3600 + TOINT(_trgt_date[4]) * 60 + TOINT(_trgt_date[5]);
	_base_time = TOINT(_base_date[3]) * 3600 + TOINT(_base_date[4]) * 60 + TOINT(_base_date[5]);

	if (_trgt_time < _base_time) {
		_TIME_DIFF = _trgt_time - _base_time + 86400;
	} else {
		_TIME_DIFF = _trgt_time - _base_time;
	}

	_result = (_DATE_DIFF,_TIME_DIFF);
	_result;
}

J_DATE
{	//------------------------------------------------------------------------------
	//	ユリウス(Julius)暦を元に日数を返す
	//------------------------------------------------------------------------------
	_y = TOINT(_argv[0]);
	_m = TOINT(_argv[1]);
	_d = TOINT(_argv[2]);

	_result = _d-32075+TOINT(1461*(_y+4800+TOINT((_m-14)/12))/4)+TOINT(367*(_m-2-TOINT((_m-14)/12)*12)/12)-TOINT(3*(TOINT((_y+4900+TOINT((_m-14)/12))/100))/4);
	_result;
}

OnNotifyInternationalInfo
{	//------------------------------------------------------------------------------
	//	時差情報の取得(SSP限定)
	//------------------------------------------------------------------------------
	//実際に使用するときに扱いやすいよう、時差を秒数に変換
	TimeZoneDiff = reference[0] * 60;
}

//======================================================================================
//	その他(ゴースト内部処理で必要になるもの、あると便利なもの、等)
//======================================================================================
OnSecondChange
{	//------------------------------------------------------------------------------
	//	OnSecondChangeイベント。これがないといろいろ動かない。
	//------------------------------------------------------------------------------
	if (SavedScript != '') {
		//予約済みスクリプトの再生と消去。
		SavedScript  = RE_REPLACE(SavedScript,'(\\e|\\t)','');
		_SavedScript = SavedScript
		"\t%(_SavedScript)\![raise,On_ValueSet,SavedScript,'']\e";
		return;
	}

	//OnSecondChangeイベントのタイミングで他に何か処理することがある場合は、OnSecondChangeSub内に記述
	CallFunction('SecondChangeSub');
}

CallFunction
{	//------------------------------------------------------------------------------
	//	存在するかどうか分からない関数を呼び出す。
	//	ISFUNC→EVALの流れをひとまとめにしたもの。
	//------------------------------------------------------------------------------
	_Function = _argv[0];
	_Value	  = _argv[1,_argc-1];
	_Function_EVAL = _Function;

	//引数が指定されていたら引数をセットする
	if (ARRAYSIZE(_Value) > 0) { _Function_EVAL = "%(_Function)(%(CHR(0x22))%(_Value)%(CHR(0x22)))"; }

	//指定された関数を呼び出す
	if (ISFUNC(_Function)){ EVAL(_Function_EVAL); }
}

OnAnchorSelect
{	//------------------------------------------------------------------------------
	//	アンカーがクリックされたときの処理(URLジャンプなど)
	//	AYAYA03の、Tips→アンカータグからURLジャンプ、より。
	//	https://emily.shillest.net/ayaya/?cmd=read&page=Tips%2F%E3%82%A2%E3%83%B3%E3%82%AB%E3%83%BC%E3%82%BF%E3%82%B0%E3%81%8B%E3%82%89URL%E3%82%B8%E3%83%A3%E3%83%B3%E3%83%97&word=OnAnchorSelect
	//------------------------------------------------------------------------------
	_id = reference[0];
	// アンカーのIDの冒頭に「http://~」があればWebサイトを開く。
	if (RE_MATCH(_id, '(http|https|ftp)://(.+)')) {
		_url = EscapeText(_id);
		"\C\j[%(_url)] \e";
		// それ以外はIDと同じ名前のイベントへジャンプ
	} else {
		CallFunction('_id')
	}
}

On_ValueSet
{	//------------------------------------------------------------------------------
	//	スクリプト内で\![raize,On_ValueSet]で呼び出す。
	//	指定された変数に指定された値をセットする。引数は省略不可。
	//------------------------------------------------------------------------------
	EVAL("%(reference[0]) = %(reference[1])");
}

ABS
{	//------------------------------------------------------------------------------
	//絶対値の取得
	//------------------------------------------------------------------------------
	if (_argv[0] < 0) {
		_argv[0] *= -1
	}
	_argv[0]
}

DelTree
{	//------------------------------------------------------------------------------
	//ディレクトリ削除関数(再帰あり)
	//------------------------------------------------------------------------------
	_dirname = _argv[0];	//引数を分かりやすい名前の変数に代入

	_attr_array = FATTRIB(_dirname);	//ファイル属性を取得

	if (_attr_array == -1){
		//ファイルが存在しない、などの場合は-1を返して終了
		-1;
		return;
	} elseif (_attr_array[2]==1) {	//対象がディレクトリなら、ディレクトリ削除処理実行
		while 1 {
			//無限ループ(ディレクトリを確実に消去するため)
			_files = FENUM(_dirname,'|');	//ディレクトリ内ファイル・ディレクトリ一覧取得
			_files = REPLACE(_files,CHR(92),CHR(47));	// \を/に置換
			_file_array = SPLIT(_files,'|');

			if (ARRAYSIZE(_file_array) == 0) {
				//ディレクトリ内に何もなければディレクトリ削除実行。
				//RMDIR関数を実行し、その結果をそのまま返す。
				RMDIR(_dirname);
				return;
			} else {
				//取得した一覧を元にファイル・ディレクトリ削除実行
				foreach _file_array; _filename {
					_targetfilename = "%(_dirname)/%(_filename)";	//処理すべきファイル・ディレクトリ名を生成
					void DelTree(_targetfilename);	//再帰呼び出し
				}
			}
		}
	} elseif (_attr_array[2]==0){
		//普通のファイルなら単純に削除。FDEL関数の結果をそのまま返す。
		FDEL(_dirname);
		return;
	} else {
		//その他、状況が不明な場合は0を返して終了
		0;
		return;
	}
}


CheckFileCharset
{	//------------------------------------------------------------------------------
	// ファイルの文字コードを調べる
	// 引数: ファイルパス
	// 戻り値: "UTF-8", "Shift_JIS"
	//------------------------------------------------------------------------------
	_path = _argv[0];
	_result  = 'UTF-8'; //UTF-8をデフォルト値とする
	if FOPEN(_path, "rb") {
		// 1. BOMチェック(UTF-8) BOMがあったらUTF-8確定
		_BOM = FREADBIN(_path,3);


		if _BOM != CHR(0xEF,0xBB,0xBF) {
			void FSEEK(_path, 0, 'start');
			_max_scan   = 16384;	//最大読み込み範囲
			_utf8_count = 0;	//UTF-8に合致した数
			_utf8_limit = 65536;	//この数以上UTF-8に合致する文字があれば、ファイル全体をUTF-8と見做す

			while (_utf8_count < _utf8_limit) {
				if (FTELL(_path) > _max_scan) {
					break;
				}

				_s1 = FREADBIN(_path,1);
				if (_s1 == -1) { break; } //ファイル終端に達したらブレイク
				if (_s1 == '') { break; } //正常に読み込めないときはブレイク

				_b1 = CHRCODE(_s1);
				if (_b1 <= 0x7F){
					continue; //ASCII文字は無視
				} else {
					_b2 = CHRCODE(FREADBIN(_path,1)); //2バイト目を読み込む
					_b3 = 0x88; //適当にUTF-8範囲内の数値をセットしておく
					_b4 = 0x88; //適当にUTF-8範囲内の数値をセットしておく
					

					//判定フラグ
					_b1_OK = 0;
					_b2_OK = 0;
					_b3_OK = 0;
					_b4_OK = 0;

					if ( 0xC2 <= _b1 && _b1 <= 0xDF ) {
						//2バイト文字(0xC2-0xDF)のチェック
						_b1_OK = 1; //1バイト目が2バイト文字の1バイト目
					} elseif ( 0xE0 <= _b1 && _b1 <= 0xEF ) {
						//3バイト文字(0xE0-0xEF)のチェック
						_b1_OK = 1; //1バイト目が3バイト文字の1バイト目
						_b3 = CHRCODE(FREADBIN(_path,1)); //3バイト目を読み込む
					} elseif ( 0xF0 <= _b1 && _b1 <= 0xF4 ) {
						//4バイト文字(0xF0-0xF4)のチェック
						_b1_OK = 1; //1バイト目が4バイト文字の1バイト目
						_b3 = CHRCODE(FREADBIN(_path,1)); //3バイト目を読み込む
						_b4 = CHRCODE(FREADBIN(_path,1)); //4バイト目を読み込む
					}

					//2バイト目と3バイト目がUTF-8の規則(0x80-0xBF)に適合しているかチェック
					_b2_OK = Is_UTF8_Trailing(_b2,_b1);
					_b3_OK = Is_UTF8_Trailing(_b3);
					_b4_OK = Is_UTF8_Trailing(_b4);

					//UTF-8かどうかの判定
					if (_b1_OK && _b2_OK && _b3_OK && _b4_OK) {
						_utf8_count++; //UTF-8文字数をカウントアップ
					} else {
						//UTF-8ではあり得ない並びになっていたらシフトJISとして判定
						_result = "Shift_JIS";
						break;
					}
				}
			}
		}
		FCLOSE(_path);
	}
	_result;
}

Is_UTF8_Trailing
{	//UTF-8の2バイト目以降をチェックする補助関数
	_value = _argv[0];
	_b1    = 0;

	if (_argc == 2) {
		_b1 = _argv[1];
	}

	_upper = 0xBF;
	_lower = 0x80;

	if (_b1 == 0xE0 ) {
		//2バイト目の下限を設定
		_lower = 0xA0;
	} elseif (_b1 == 0xF0 ) {
		//2バイト目の下限を設定
		_lower = 0x90;
	} elseif (_b1 == 0xF4 ) {
		//2バイト目の上限を設定
		_upper = 0x8F;
	}

	(_lower <= _value && _value <= _upper);
}

//======================================================================================
//	デバッグ関連
//======================================================================================
On_enable_debug
{	//------------------------------------------------------------------------------
	//「SHIORIデバッグモード有効」のチェック状態を控える
	//------------------------------------------------------------------------------
	DEBUGMODE = reference[0];
}

On_Debug
{	//------------------------------------------------------------------------------
	//	SEEDなどから送られてきたイベントを処理する
	//------------------------------------------------------------------------------
	EVAL(CHR(0x22)+reference[0]+CHR(0x22));
}

Debug_Key
{	//------------------------------------------------------------------------------
	//	デバッグ時に使用するキー入力
	//------------------------------------------------------------------------------
	case _argv[0] {
		when 'r' {
			//SHIORIを再読み込み
			SavedScript = '\1\s[10]\0\s[0]AI辞書リロード完了。';
			'\t\1\s[10]\0\s[0]AI辞書をリロード。\![reload,shiori]\e';
		}

		when 's' {
			//シェルを再読み込み
			SavedScript = '\1\s[10]\0\s[0]シェルリロード完了。';
			'\t\1\s[10]\0\s[0]シェルをリロード。\![reload,shell]\e';
		}

		when 'g' {
			//ゴースト全体を再読み込み
			SavedScript = '\1\s[10]\0\s[0]ゴーストリロード完了。';
			'\t\1\s[10]\0\s[0]ゴースト全体をリロード。\![reload,ghost]\e';
		}

		when 't','f12' { OnAiTalk; }		//通常のランダムトーク
		when 'd' { Talk_Debug('replay'); }	//直前に指定したAIトークをもう一度再生する
		when 'n' { Talk_Debug('next'); }	//次のAIトークを再生する
		when 'p' { Talk_Debug('prev'); }	//前のAIトークを再生する
		when 'l' { Talk_Debug('select'); }	//スライダーで指定したAIトークを再生する
		when 'i' { Talk_Debug('input'); }	//インプットボックスで指定したAIトークを再生する
		when 'home','end','pageup','pagedown' { Talk_Debug(_argv[0]); }	//最初・最後・10個前・10個後のAIトークを再生する
	}
}

Talk_Debug
{	//------------------------------------------------------------------------------
	//	条件によって再生するAIトークを変える
	//------------------------------------------------------------------------------
	TalkCount++;	//トークカウンタをインクリメント

	case _argv[0] {
		when 'next'	{ Talk_ID++; }		//トークIDをインクリメント(次のAIトーク)
		when 'prev'	{ Talk_ID--; }		//トークIDをデクリメント(前のAIトーク)
		when 'home'	{ Talk_ID = 0; }	//トークIDを0に(最初のAIトーク)
		when 'pageup'	{ Talk_ID -= 10; }	//トークIDを+10(10個先のAIトーク)
		when 'pagedown'	{ Talk_ID += 10; }	//トークIDを-10(10個前のAIトーク)
		when 'end'	{ Talk_ID = ARRAYSIZE(RandomTalkArray)-1; }	//トークIDをランダムトーク配列の要素数に(最後のAIトーク)
		when 'select' {
			//AIトークを指定するためのスライダーを開く
			"\0\![open,sliderinput,On_TalkSelect,0,%(Talk_ID),0,%(ARRAYSIZE(RandomTalkArray)-1)]\e";
			return;
		}
		when 'input' {
			//AIトークを指定するためのインプットボックスを開く
			"\0\![open,inputbox,On_TalkSelect,,%(Talk_ID)]\e";
			return;
		}
	}

	//トークIDが範囲外の場合、範囲内に戻す
	if (Talk_ID > ARRAYSIZE(RandomTalkArray)-1) {
		Talk_ID = 0;
	} elseif (Talk_ID < 0) {
		Talk_ID = ARRAYSIZE(RandomTalkArray)-1;
	}

	//スクリプト内の%()を展開し、再生する
	_script = EVAL(CHR(0x22)+STRFORM("\_qID:$04d\c\b[-1]\_q%(RandomTalkArray[Talk_ID])",Talk_ID)+CHR(0x22));
	_script;
}

On_TalkSelect
{	//------------------------------------------------------------------------------
	//	スライダーで指定されたAIトークを再生する
	//------------------------------------------------------------------------------
	Talk_ID = TOINT(reference[0]);
	Talk_Debug;
}

UserInputEx
{	//------------------------------------------------------------------------------
	//	インプットBOXに指定の文字列が入力されたらデバッグモードをオン・オフする
	//	手順:ベースウェアでSendボックスを開く→\![open,inputobox]と入力
	//	  →開いたインプットボックスに、EnterDebugModeまたはExitDebugModeと入力
	//------------------------------------------------------------------------------
	if (reference[1] == 'EnterDebugMode') {
		DEBUGMODE = 1;
		'\0\s[0]デバッグモードを設定。\e';
	} elseif (reference[1] == 'ExitDebugMode') {
		DEBUGMODE = 0;
		'\0\s[0]デバッグモードを解除。\e';
	}
}

OnUkadocScriptExample
{	//------------------------------------------------------------------------------
	//	UKADOCのさくらスクリプトページにあるサンプルスクリプトを実行する
	//	UKADOC:https://ssp.shillest.net/ukadoc/manual/
	//------------------------------------------------------------------------------
	_script = SHIORI3FW.EscapeDangerousTags(reference[0]);
	EVAL(_script);
}

//======================================================================================
//	その他
//======================================================================================
OnNotifyUserInfo
{	//------------------------------------------------------------------------------
	//ユーザ情報取得
	//------------------------------------------------------------------------------
	UserNickName_Baseware = reference[0]
	UserName_Baseware     = reference[1]
	Birthday_Baseware     = reference[2]
	_DateTimeDiff = DateTimeCalc("%(Birthday_Baseware[0]),%(Birthday_Baseware[1]),%(Birthday_Baseware[2]),0,0,0","%(year),%(month),%(day),0,0,0");
	UserAge = _DateTimeDiff[0] / 365;
}

AYATEMPLATE.EscapeText
{	//------------------------------------------------------------------------------
	// 「[[はろーYAYAわーるど>http://ms.shillest.net/yayame.xhtml]]」にあるエスケープ関数。
	//------------------------------------------------------------------------------
	_r = _argv[0]
	if (RE_SEARCH(_r,'[,"\[\]]')) {
		'"' + REPLACE(_r,'"','''') + '"';
	}
	else {
		_r;
	}
}

//******************************************************************************
// SimpleYAYAテンプレート独自追加分をコピー
//******************************************************************************

//関数名が長いため短縮
EscapeAllTags {SHIORI3FW.EscapeAllTags(_argv)}
EscapeDangerousTags {SHIORI3FW.EscapeDangerousTags(_argv)}
IsGhostExist {SHIORI3FW.IsGhostExist(_argv)}
RefreshFMOTable {SHIORI3FW.RefreshFMOTable(_argv)}
FMOCache {SHIORI3FW.FMOCache}
SetDelayEvent {SHIORI3FW.SetDelayEvent(_argv)}
EscapeText {AYATEMPLATE.EscapeText(_argv)}
yaya_library.dic (44,104 bytes)   

Activities

Add Note

View Status
Note
Upload Files
Maximum size: 1,953 KiB

Attach files by dragging & dropping, selecting or pasting them.

送信ボタンを押す前に / Before pushing "Send" button

  • スパム避けのためにパスワードが必要です。送信ボタンを押すとユーザー名とパスワードを聞かれますので、それぞれ "bts" と入力してください。
  • To send report, please input "bts" into username and password box in popup dialog.

Issue History

Date Modified Username Field Change
2026-03-31 11:10 guest New Issue
2026-03-31 11:10 guest File Added: yaya_library.dic