//******************************************************************************
/*
SHIORI/3.0制御スクリプト for YAYA

使用可能な変数

SHIORI3FW.Status
　初期起動ステータス Runで初期化済み

SHIORI3FW.Eventid
　イベントID

SHIORI3FW.LastTalk
　直前のトーク（すべて）

SHIORI3FW.LastAITalk
　直前のランダムトーク

SHIORI3FW.LastSurface
　直前に表示していたサーフィス SHIORI3FW.LastSurface[0] がさくら側

SHIORI3FW.IsVisible
　現在サーフィスが表示されているかどうか(1/0) SHIORI3FW.IsVisible[0] がさくら側

SHIORI3FW.HWnd
SHIORI3FW.BalloonHWnd
　キャラウインドウ・バルーンのHWND。 SHIORI3FW.HWnd[0] がさくら側

SHIORI3FW.UniqueID
　Auth.SSTPに使用するID

SHIORI3FW.Capability
　ID: capabilityで来たベースウェアのヘッダ処理能力：汎用配列

SHIORI3FW.GhostName
　ゴースト名★

SHIORI3FW.ShellName
SHIORI3FW.ShellPath
　シェル名とパス★

SHIORI3FW.BalloonName
SHIORI3FW.BalloonPath
　バルーン名とパス★

使用可能な疑似イベント

OnSHIORI3FW.SurfaceRestore
　OnSurfaceRestoreの本体非依存版

OnSHIORI3FW.ChangeSelfInfo
　上記★マークがついた変数が更新されたときに呼び出される

*/
//******************************************************************************


//------------------------------------------------------------------------------
//以下のdefineは基本的に弄らないで下さい
//------------------------------------------------------------------------------

// チェイン
#globaldefine	CHAIN{{		{{CHAIN	/* どうも誤記される場合があるようなので */
#globaldefine	CHAIN}}		}}CHAIN	/* 同上 */
#globaldefine	{{CHAIN		{ switch CHAIN.Index {
#globaldefine	}}CHAIN		} CHAIN.Index++ }

#define			CHAIN_DELIM		':chain='
#define			EVAL_DELIM		':eval='

//******************************************************************************
// load
//******************************************************************************

//------------------------------------------------------------------------------
//関数名：load
//機能　：ロード時の処理
//------------------------------------------------------------------------------
load
{
	// 文字/文字列定数
	C_CRLF2 = CHR(0xd,0xa,0xd,0xa)
	C_CRLF = CHR(0xd,0xa)
	C_BYTE1 = CHR(0x1)

	REQ.COMMAND   = "LOAD"
	REQ.PROTOCOL  = "AYA/5.0"
	var.req.key   = (IARRAY, "Path")
	var.req.value = (IARRAY, _argv[0])
	var.req.rawvalue = (IARRAY,_argv[0])
	
	SHIORI3FW.Path = _argv[0]
	
	var.lib.key = IARRAY
	var.lib.value = IARRAY
	var.lib.result = ''
	
	if (FSIZE(GETSETTING('coreinfo.savefile')) <= 0) && (FSIZE(GETSETTING('coreinfo.savefile') + '.ays') <= 0) {
		if FSIZE('aya5_variable.cfg') > 0 {
			RESTOREVAR('aya5_variable.cfg')
			FDEL('aya5_variable.cfg')
		}
		elseif FSIZE('aya_variable.cfg') > 0 {
			RESTOREVAR('aya_variable.cfg')
			FDEL('aya_variable.cfg')
		}
		SAVEVAR()
	}

	// 初回起動時の初期化
	if ISVAR('aitalkinterval') == 0 {
		aitalkinterval   = TALK_INTERVAL
	}
	if ISVAR('ghostupmin_last') == 0 {
		ghostupmin_last = 0
	}
	if ISVAR('ghostuptimes') == 0 {
		ghostuptimes = 0
	}
	ghostuptimes += 1

	// 毎回起動時の初期化
	SHIORI3FW.ResetAITalkInterval()
	
	_sec = GETSECCOUNT()
	
	SHIORI3FW.SaveVarCount = 0
	SHIORI3FW.LastTalk     = ''
	SHIORI3FW.LastTalkTime = _sec
	SHIORI3FW.LastAITalk   = ''
	SHIORI3FW.LastAITalkTime = _sec
	SHIORI3FW.TalkEndTime  = _sec
	SHIORI3FW.IsTalking    = 0
	
	if GETTYPE(SHIORI3FW.LastSurface) != 4 {
		SHIORI3FW.LastSurface  = (0,10)
		SHIORI3FW.IsVisible    = (1,1)
	}
	
	SHIORI3FW.HWnd         = (0,0)
	SHIORI3FW.HWndOld      = (0,0)
	SHIORI3FW.BalloonHWnd  = (0,0)
	SHIORI3FW.UniqueID     = ''
	SHIORI3FW.DebugMode    = 1
	SHIORI3FW.CanTalkFlag  = 1
	
	SHIORI3FW.Eventid = ''
	SHIORI3FW.EventidOld = ''
	SHIORI3FW.EventidTranslate = ''
	SHIORI3FW.EventidTranslateOld = ''
	SHIORI3FW.SecurityLevel = ''
	
	SHIORI3FW.ErrorDescription = ''
	SHIORI3FW.ErrorLevel = ''
	SHIORI3FW.LastErrorDescription = ''
	
	SAORI.DllList          = IARRAY
	basewarename           = ''
	basewarenameex         = ''
	sender                 = ''
	ghostbootmin           = GETSECCOUNT() / 60

	S_CHARSET = GETSETTING('charset.output')
	
	status = ''
	tempvarname = IARRAY
	
	array_eventtranslate = ASORT('string,ascending',GETFUNCLIST('OnGhostEventTranslate'))
	array_translateinternal = ASORT('string,ascending',GETFUNCLIST('OnTranslateInternal'))

	SHIORI3FW.InitChain
	SHIORI3FW.InitAdditionalReturn

	SHIORI3FW.SetDelayEvent('',0)

	//過去互換用（ascii変数）
	if ISFUNC('restore_old_variables') {
		EVAL('restore_old_variables')
	}
	
	AyaLoad.FuncEnumExec('OnSystemLoad','ascending')
	AyaLoad.FuncEnumExec('OnGhostLoad','ascending')
	
	_ft = <<'
	On_version
	On_craftman
	On_craftmanw
	'>>
	_fa = SPLIT(_ft,C_CRLF)
	
	foreach _fa;_f {
		if ISFUNC(_f) {
			LOGGING("警告：関数/イベント %(_f) はシステム辞書で上書きされます")
		}
	}
}

//******************************************************************************
//unload
//******************************************************************************

//------------------------------------------------------------------------------
//関数名：unload
//機能　：終了処理
//------------------------------------------------------------------------------
unload
{
	REQ.COMMAND   = "UNLOAD"
	REQ.PROTOCOL  = "AYA/5.0"
	var.req.key   = IARRAY
	var.req.value = IARRAY
	var.req.rawvalue = IARRAY
	
	ghostupmin_last = ghostupmin_total()

	AyaLoad.FuncEnumExec('OnGhostUnload','descending')
	AyaLoad.FuncEnumExec('OnSystemUnload','descending')
	
	// 保存する必要のない変数を削除
	_var = <<'
array_eventtranslate
array_translateinternal
REQ.COMMAND
REQ.PROTOCOL
var.req.key
var.req.value
var.req.rawvalue
LIB.PROTOCOL
LIB.STATUSCODE
var.lib.key
var.lib.value
var.lib.result
SAORI.DllList
ghostexlist
ghostexcount
selfname
selfname2
sakuraname
keroname
basewarename
basewarenameex
sender
S_CHARSET
installedghostlist
installedsakuralist
installedkerolist
ascii
displaybpp
displaywidth
displayheight
uniqueid
ghostbootmin
status
res_securitylevel
res_reference
res_marker
marker
res_valuenotify
refold
refold0
SHIORI3FW.Path
SHIORI3FW.LastAITalk
SHIORI3FW.LastAITalkTime
SHIORI3FW.LastTalk
SHIORI3FW.LastTalkTime
SHIORI3FW.TalkEndTime
SHIORI3FW.CanTalkFlag
SHIORI3FW.IsSurfaceRestoreComplete
SHIORI3FW.IsAITalkComplete
SHIORI3FW.SaveVarCount
SHIORI3FW.HWnd
SHIORI3FW.HWndOld
SHIORI3FW.BalloonHWnd
SHIORI3FW.UniqueID
SHIORI3FW.Eventid
SHIORI3FW.EventidOld
SHIORI3FW.EventidTranslate
SHIORI3FW.EventidTranslateOld
SHIORI3FW.SecurityLevel
SHIORI3FW.DelayEventTime
SHIORI3FW.DelayEvent
SHIORI3FW.DelayEventReference
SHIORI3FW.GhostName
SHIORI3FW.ErrorDescription
SHIORI3FW.ErrorLevel
SHIORI3FW.LastErrorDescription
SHIORI3FW.Capability
C_CRLF2
C_CRLF
C_BYTE1
'>>
	//以下は保存
	//SHIORI3FW.ShellName
	//SHIORI3FW.ShellNameOld
	//SHIORI3FW.ShellPath
	//SHIORI3FW.BalloonName
	//SHIORI3FW.BalloonPath
	//SHIORI3FW.LastSurface
	//SHIORI3FW.IsVisible

	ERASEVAR(SPLIT(_var,C_CRLF))
	
	_n = ARRAYSIZE(tempvarname)
	for _i = 0 ; _i < _n ; _i++ {
		ERASEVAR("%(tempvarname[_i])")
	}
	ERASEVAR('tempvarname')
	
	SHIORI3FW.ClearChainVariable
	SHIORI3FW.ClearReferenceAndPassThruInsVariable
	SHIORI3FW.ClearValueexVariable
	SHIORI3FW.ClearAdditionalReturnVariable
}

//------------------------------------------------------------------------------
//関数名：AyaTest.Eval
//機能　：受け取った文字列をEVALして返す
//------------------------------------------------------------------------------
AyaTest.Eval
{
	_array = SPLIT(_argv[0],C_CRLF)
	_result = ''
	_n = ARRAYSIZE(_array)
	
	for _i = 0 ; _i < _n ; _i++ {
		if _array[_i] {
			_result += EVAL(_array[_i])
		}
	}
	_result
}

//------------------------------------------------------------------------------
//関数名：AyaLoad.FuncEnumExec
//機能　：関数リストを作成しソートして実行
//引数　：0=イベント 1=ソート順(ascending,descending)
//------------------------------------------------------------------------------
AyaLoad.FuncEnumExec : void
{
	_a = GETFUNCLIST(_argv[0])
	_a = ASORT('string,' + _argv[1],_a)
	
	_n = ARRAYSIZE(_a)
	for _i = 0 ; _i < _n ; _i++ {
		EVAL(_a[_i])
	}
}


//******************************************************************************
//request
//******************************************************************************
//------------------------------------------------------------------------------
//関数名：request
//機能　：受け取った文字列をSHIORIリクエストヘッダとして解析した後、
//        OnRequestを活性化します
//------------------------------------------------------------------------------
request
{
	//For TEST
	//test(_argv[0])
	//return
	
	if SUBSTR(_argv[0],0,3) == '?? ' { //玉でのテスト用
		'!! ' + JOIN(AyaTest.Eval(ERASE(_argv[0],0,3)),',')
		return
	}
	
	_reqdata = _argv[0]
	_linestart = 0
	_lineend = STRSTR(_reqdata, C_CRLF, _linestart)
	
	if _lineend <= 0 {
		//1行目すらない！
		"SHIORI/3.0 400 Bad Request%(C_CRLF)Charset: %(S_CHARSET)%(C_CRLF2)"
		return
	}
	_lin = SUBSTR(_reqdata,_linestart,(_lineend - _linestart))

	// リクエスト種別とプロトコル名の取得　エラーなら400
	REQ.COMMAND  = _lin[0," SHIORI"]
	REQ.PROTOCOL = "SHIORI" + _lin[1," SHIORI"]

	// リクエストヘッダの取得
	var.req.key   = IARRAY
	var.req.value = IARRAY
	var.req.rawvalue = IARRAY
	
	_linenum = 0
	_idparsed = 0
	
	status = ''
	SHIORI3FW.Eventid=''
	SHIORI3FW.SecurityLevel='Internal' //互換性のためデフォルトはInternal
	
	while _lineend > _linestart {
		//行分割
		_linestart = _lineend + 2
		_lineend = STRSTR(_reqdata, C_CRLF, _linestart)
		
		//空行もしくはみつからなかった
		if _lineend <= _linestart {
			break
		}
		
		_linenum += 1
		if SHIORI3FW.REQUEST_LINES_LIMIT {
			if _linenum > SHIORI3FW.REQUEST_LINES_LIMIT {
				if _idparsed {
					break
				}
			}
		}
		
		_lin = SUBSTR(_reqdata,_linestart,(_lineend - _linestart))
				
		// キーと値を取得
		_len = STRLEN(_lin)
		_pos = STRSTR(_lin,": ",0)
		var.req.key ,= (_key = SUBSTR(_lin,0,_pos))
		_value       = SUBSTR(_lin,(_pos + 2),(_len - _pos - 2))
		
		if var.req.key == '' {
			break
		}

		// イベントID名称を取得
		if _key == "Charset" {
			if S_CHARSET != _value {
				void SETSETTING('charset.output',_value)
				S_CHARSET = _value
			}
		}
		elseif _key == "ID" {
			// 取得　名前先頭が"On"でないなら付加
			SHIORI3FW.Eventid = _value
			if SUBSTR(SHIORI3FW.Eventid, 0, 2) != "On" {
				SHIORI3FW.Eventid = "On_" + SHIORI3FW.Eventid
			}
			
			if SHIORI3FW.Eventid == 'OnAiTalk' || SHIORI3FW.Eventid == 'OnAITalk' {
				SHIORI3FW.Eventid = 'OnAITalkNewEvent'
			}

			SHIORI3FW.EventidTranslate = SHIORI3FW.TranslateEvent(SHIORI3FW.Eventid)

			// ハンドラが無い場合は即返る
			if !ISFUNC(SHIORI3FW.EventidTranslate) && !SHIORI3FW.IsImportantEvent {
				SHIORI3FW.MakeEmptyResponse(SHIORI3FW.Eventid)
				--
				C_CRLF
				
				return
			}
			_idparsed = 1
		}
		// セキュリティレベル
		elseif _key == 'SecurityLevel' {
			SHIORI3FW.SecurityLevel = _value
		}
		// ベースウェア名取得
		elseif _key == 'Sender' {
			if basewarenameex == '' {
				basewarenameex = _value
			}
			basewarename = _value
			sender = _value
		}
		//Status
		elseif _key == 'Status' {
			status = _value
		}

		//特定のイベントを除きExternalなら204で返る
		if SHIORI3FW.SecurityLevel == 'External' && SHIORI3FW.Eventid != 'OnXUkagakaLinkOpen' {
			SHIORI3FW.MakeEmptyResponse
			return
		}

		// キーと値を記憶
		var.req.rawvalue ,= _value
		
		if SHIORI3FW.AUTO_DATA_CONVERT {
			if ISINTSTR(_value) {
				var.req.value ,= TOINT(_value)
			}
			elseif ISREALSTR(_value) {
				var.req.value ,= TOREAL(_value)
			}
			else {
				var.req.value ,= REPLACE(_value, C_BYTE1, ",")	// バイト値1はカンマ化してしまう
			}
		}
		else {
			var.req.value ,= TOAUTOEX(_value)
		}
	}

	OnRequest
}

TOAUTOEX
{
	_v = _argv[0]
	if TOSTR(TOAUTO(_v)) == _v {
		TOAUTO(_v)
		return
	}
	_v
}

SHIORI3FW.TranslateEvent
{
	_result = _argv[0]
	_as = ARRAYSIZE(array_eventtranslate)
	if _as > 0 {
		_ev = ''
		for _i = 0 ; _i < _as ; _i++ {
			void EVAL("_ev = %(array_eventtranslate[_i])(_result)")
			if STRLEN(_ev) > 0 {
				_result = _ev
			}
		}
	}
	_result
}

//------------------------------------------------------------------------------
//関数名：OnRequest
//機能　：受け取ったリクエストに応じた処理を行います
//------------------------------------------------------------------------------
OnRequest
{
	// コマンド別に処理分岐
	case REQ.COMMAND {
	when "NOTIFY" {
		// NOTIFYリクエスト
		void SHIORI3FW.RaiseIDEvent
		"SHIORI/3.0 204 No Content%(C_CRLF)Charset: %(S_CHARSET)%(C_CRLF)"
		--
		SHIORI3FW.AssembleReferenceHeader(1)
	}

	when "GET" {
		// GETリクエスト
		if (_result = SHIORI3FW.RaiseIDEvent) == "" {
			SHIORI3FW.MakeEmptyResponse(SHIORI3FW.Eventid)
		}
		else {
			"SHIORI/3.0 200 OK%(C_CRLF)Sender: AYA%(C_CRLF)Charset: %(S_CHARSET)%(C_CRLF)/
				Value: %(_result)%(C_CRLF)"
			--
			SHIORI3FW.AssembleReferenceHeader(0)
			
			if SHIORI3FW.Eventid != 'OnTranslate' {
				SHIORI3FW.EventidOld = SHIORI3FW.Eventid
				SHIORI3FW.EventidTranslateOld = SHIORI3FW.EventidTranslate
			}
			
			SHIORI3FW.GetLastErrorLog
			
			if SHIORI3FW.LastErrorDescription != SHIORI3FW.ErrorDescription {
				SHIORI3FW.LastErrorDescription = SHIORI3FW.ErrorDescription
				
				SHIORI3FW.PushAdditionalReturn('ErrorLevel',SHIORI3FW.ErrorLevel)
				SHIORI3FW.PushAdditionalReturn('ErrorDescription',SHIORI3FW.ErrorDescription)
			}
		}
	}

	others
		// 未知のリクエスト
		"SHIORI/3.0 400 Bad Request%(C_CRLF)"
	}

	--
	SHIORI3FW.GetAdditionalReturns
	--
	C_CRLF
}

//------------------------------------------------------------------------------
//関数名：SHIORI3FW.GetLastErrorLog
//機能　：エラーログに表示すべき文字列を作成します
//------------------------------------------------------------------------------
SHIORI3FW.GetLastErrorLog
{
	_errors = GETERRORLOG()
	
	_errorlevel_text = 'SHIORI3FW.ERRORLOGLEVEL'
	_errorlevel = 2
	
	if _errorlevel_text == 'note' {
		_errorlevel = 0
	}
	elseif _errorlevel_text == 'warning' {
		_errorlevel = 1
	}
	
	SHIORI3FW.ErrorDescription = ''
	SHIORI3FW.ErrorLevel = ''
	
	foreach _errors; _error {
		_el = 0
		
		if ( STRSTR(_error,'error',0) >= 0 ) {
			_el = 2
		}
		elseif ( STRSTR(_error,'warning',0) >= 0 ) {
			_el = 1
		}
		
		if _el < _errorlevel {
			continue
		}
		
		if STRLEN(SHIORI3FW.ErrorDescription) > 0 {
			SHIORI3FW.ErrorDescription += CHR(1)
			SHIORI3FW.ErrorLevel += CHR(1)
		}
		
		SHIORI3FW.ErrorDescription += _error
		
		if _el == 0 {
			SHIORI3FW.ErrorLevel += 'info'
		}
		elseif _el == 1 {
			SHIORI3FW.ErrorLevel += 'warning'
		}
		else {
			SHIORI3FW.ErrorLevel += 'critical'
		}
	}
	
}

//------------------------------------------------------------------------------
//関数名：SHIORI3FW.InitAdditionalReturn
//機能　：追加ヘッダ用変数を初期化します
//------------------------------------------------------------------------------
SHIORI3FW.InitAdditionalReturn : void
{
	SHIORI3FW.AdditionalReturn.String = ''
}

//------------------------------------------------------------------------------
//関数名：SHIORI3FW.PushAdditionalReturn
//機能　：ヘッダを追加します (設定はリクエストごとにリセットされます)
//引数　：_argv[0] ヘッダ名  _argv[1] 値
//------------------------------------------------------------------------------
SHIORI3FW.PushAdditionalReturn : void
{
	_key="%(_argv[0]): "
	_argv[0]=IARRAY
	SHIORI3FW.AdditionalReturn.String += _key+TOSTR(_argv)+C_CRLF
}

//------------------------------------------------------------------------------
//関数名：SHIORI3FW.Push_X_SSTP_PassThru
//機能　：X-SSTP-PassThruヘッダを追加します (設定はリクエストごとにリセットされます)
//引数　：_argv[0] ヘッダ名  _argv[1] 値
//------------------------------------------------------------------------------
SHIORI3FW.Push_X_SSTP_PassThru : void
{
	_h = 'X-SSTP-PassThru-' + TOSTR(_argv[0])
	SHIORI3FW.PushAdditionalReturn(_h,_argv[1])
}

//------------------------------------------------------------------------------
//関数名：SHIORI3FW.ClearAdditionalReturnVariable
//機能　：追加ヘッダ用変数を削除します
//------------------------------------------------------------------------------
SHIORI3FW.ClearAdditionalReturnVariable : void
{
	ERASEVAR('SHIORI3FW.AdditionalReturn.String')
}

//------------------------------------------------------------------------------
//関数名：SHIORI3FW.GetAdditionalReturns
//機能　：追加ヘッダを展開します
//返値　：追加すべきヘッダ文字列 最後は必ず改行で終わる
//------------------------------------------------------------------------------
SHIORI3FW.GetAdditionalReturns
{
	_t=SHIORI3FW.AdditionalReturn.String
	SHIORI3FW.AdditionalReturn.String = ''
	_t
}

//------------------------------------------------------------------------------
//関数名：SHIORI3FW.AssembleReferenceHeader
//機能　：res_reference*変数を順次検査して返送用のReference文字列を作成します
//------------------------------------------------------------------------------
SHIORI3FW.AssembleReferenceHeader
{
	_is_notify = TOINT(_argv[0])
	
	_result = ""
	_before = ""
	_value = ""
	
	if _is_notify == 0 {
		_r = GETTYPEEX('res_reference')
		
		if _r != 0 {
			if _r == 4 {
				_n = ARRAYSIZE(res_reference)
				for _i = 0 ; _i < _n ; _i++ {
					_result += "Reference%(_i): %(res_reference[_i])%(C_CRLF)"
				}
			}
			else {
				_result += "Reference0: %(res_reference)%(C_CRLF)"
			}
			ERASEVAR('res_reference')
		}
		else {
			for _i = 0; _i < SHIORI3FW.RES_REF_MAX; _i++ {
				_before = _value
				
				_varname = "res_reference" + _i
				_value = EVAL(_varname)
				if _value != "" {
					_result += "Reference%(_i): %(_value)%(C_CRLF)"
					ERASEVAR(_varname);
				}
				else {
					if _before == ""; break
				}
			}
		}
		
		if (marker != "") || (res_marker != "") {
			if marker != "" {
				_result += "Marker: %(marker)%(C_CRLF)"
				ERASEVAR('marker')
			}
			else {
				_result += "Marker: %(res_marker)%(C_CRLF)"
				ERASEVAR('res_marker')
			}
		}
	}
	
	if res_valuenotify != "" {
		_result += "ValueNotify: %(res_valuenotify)"
		ERASEVAR('res_valuenotify')
	}
	
	_result
}

//------------------------------------------------------------------------------
//関数名：SHIORI3FW.IsImportantEvent
//機能　：絶対に通知すべきイベントを識別します
//引数　：_argv[0] (本当の)イベント名
//------------------------------------------------------------------------------
SHIORI3FW.IsImportantEvent
{
	if SHIORI3FW.Eventid == "OnSecondChange" {
		1
		return
	}
	elseif ISFUNC('SHIORI3EV.' + SHIORI3FW.Eventid) {
		1
		return
	}
	0
}

//------------------------------------------------------------------------------
//関数名：SHIORI3FW.RaiseIDEvent
//機能　：指定された名前の関数を実行して結果を取得します
//------------------------------------------------------------------------------
SHIORI3FW.RaiseIDEvent
{
	//reference&PassThru_Ins 変数を作成
	SHIORI3FW.MakeReferenceAndPassThruInsVariable
	
	_event = SHIORI3FW.EventidTranslate
	
	_result_internal = ''
	if SHIORI3FW.Eventid == "OnSecondChange" {
		// OnSecondChangeならランダムトーク関連処理
		_event = SHIORI3FW.ControlAITalk(_event)
		_event = SHIORI3FW.TranslateEvent(_event)
	}
	
	if _event != 'OnSecondChange' {
		if ISFUNC('SHIORI3EV.' + _event) {
			_result_internal = TOSTR(EVAL('SHIORI3EV.' + _event))
		}
	}
	
	_can_talk = SHIORI3FW.CanTalk()
	if _can_talk != SHIORI3FW.CanTalkFlag {
		if _can_talk {
			SHIORI3FW.TalkEndTime = GETSECCOUNT()
		}
		SHIORI3FW.CanTalkFlag = _can_talk
	}

	// イベント活性化
	_result = ''
	if ISFUNC(_event) {
		_result = TOSTR(EVAL(_event))
	}
	if _result == '' {
		_result = _result_internal
	}
	
	if _result != '' {
		// 遅延EVAL
		_talk = RE_SPLIT(_result,'(' + EVAL_DELIM + '|' + CHAIN_DELIM + ')')
		_delim = RE_GETSTR
		_n = ARRAYSIZE(_delim)
		
		//先に内部トランスレート
		if SHIORI3FW.Eventid != 'OnTranslate' {
			_as = ARRAYSIZE(array_translateinternal)
			if _as >= 1 {
				_tk = ''
				for _i = 0 ; _i < _as ; _i++ {
					void EVAL("_tk = %(array_translateinternal[_i])(_talk[0])")
					if STRLEN(_tk) > 0 {
						_talk[0] = _tk
					}
				}
			}
		}
		
		//後から遅延設定
		for _i = 0 ; _i < _n ; _i += 1 {
			if _delim[_i] == EVAL_DELIM {
				// 遅延EVAL
				void EVAL(_talk[_i + 1])
			}
			elseif _delim[_i] == CHAIN_DELIM {
				// チェイン処理
				SHIORI3FW.StartChainTalk(_talk[_i + 1])
			}
		}
		
		if _talk[0] != '' {
			if SHIORI3FW.Status != 'Run' {
				SHIORI3FW.Status = 'Run'
			}
			SHIORI3FW.LastTalk = _talk[0]
			if _event == 'OnAITalkNewEvent' {
				SHIORI3FW.LastAITalk = _talk[0]
				SHIORI3FW.ResetAITalkInterval()
			}
			if 'Surface' !_in_ _event {
				if SHIORI3FW.RemoveAllTags(_talk[0]) != '' {
					SHIORI3FW.LastTalkTime = GETSECCOUNT()
				}
			}
			_talk[0]
		}
	}

	// reference&PassThru_Ins 変数をクリア
	SHIORI3FW.ClearReferenceAndPassThruInsVariable
}

//------------------------------------------------------------------------------
//関数名：SHIORI3FW.MakeEmptyResponse
//機能　："204を返してはいけないリクエスト"を判定して、
//        適正な応答文字列を作成します
//引数　：_argv[0] イベント名
//------------------------------------------------------------------------------
SHIORI3FW.MakeEmptyResponse
{
	case _argv[0] {
	when "OnFirstBoot", "OnBoot", "OnWindowStateRestore", "OnGhostChanged"
		// スコープ0/1の基本サーフィスを出す必要があるイベント
		"SHIORI/3.0 200 OK%(C_CRLF)Sender: AYA%(C_CRLF)Charset: %(S_CHARSET)%(C_CRLF)/
			Value: \0\s[0]\1\s[10]\e%(C_CRLF)"
	when "OnClose"
		// 終了指示を出す必要があるイベント
		"SHIORI/3.0 200 OK%(C_CRLF)Sender: AYA%(C_CRLF)Charset: %(S_CHARSET)%(C_CRLF)/
			Value: \0\-\e%(C_CRLF)"
	others
		// 上記以外では204を返送して良い
		"SHIORI/3.0 204 No Content%(C_CRLF)Charset: %(S_CHARSET)%(C_CRLF)"
	}
}

//------------------------------------------------------------------------------
//関数名：SHIORI3FW.ResetAITalkInterval
//機能　：AIトーク発動処理カウンタのリセット
//------------------------------------------------------------------------------
SHIORI3FW.ResetAITalkInterval
{
	SHIORI3FW.LastAITalkTime = GETSECCOUNT()
	SHIORI3FW.IsSurfaceRestoreComplete = 0
	SHIORI3FW.IsAITalkComplete = 0
}

//------------------------------------------------------------------------------
//関数名：SHIORI3FW.ControlAITalk
//機能　：AIトーク発動処理、変数データの自動保存処理
//引数　：_argv[0] イベント名
//------------------------------------------------------------------------------
SHIORI3FW.ControlAITalk
{
	_return_value = _argv[0]
	
	if TOINT(reference[3]) { //喋れる
		if SHIORI3FW.DelayEventTime {
			SHIORI3FW.DelayEventTime--

			if SHIORI3FW.DelayEventTime <= 0 {
				_return_value = SHIORI3FW.DelayEvent
				if ARRAYSIZE(SHIORI3FW.DelayEventReference) {
					reference = SHIORI3FW.DelayEventReference
				}
			}
		}
		if aitalkinterval > 0 {		// トーク間隔が非0
			_sec = GETSECCOUNT()
			_diff = _sec - SHIORI3FW.LastAITalkTime
			if _diff >= aitalkinterval {	// トーク間隔の設定時間を越えた
				if SHIORI3FW.CanTalk() {
					if (_sec - SHIORI3FW.LastTalkTime) >= 6 {
						if (_sec - SHIORI3FW.TalkEndTime) >= 6 {
							if SHIORI3FW.IsAITalkComplete == 0 {
								_return_value = "OnAITalkNewEvent"
								SHIORI3FW.IsAITalkComplete = 1
							}
						}
					}
				}
			}
			elseif _diff == (aitalkinterval*2/3) { //2/3でSurfaceRestore
				if SHIORI3FW.CanTalk() {
					if (_sec - SHIORI3FW.LastTalkTime) >= 4 {
						if (_sec - SHIORI3FW.TalkEndTime) >= 2 {
							if SHIORI3FW.IsSurfaceRestoreComplete == 0 {
								_return_value = "OnSHIORI3FW.SurfaceRestore"
								SHIORI3FW.IsSurfaceRestoreComplete = 1
							}
						}
					}
				}
			}
		}
	}

	SHIORI3FW.SaveVarCount++
	if SHIORI3FW.SaveVarCount > 1800 {
		SHIORI3FW.SaveVarCount = 0
		SAVEVAR
	}

	_return_value
}

//------------------------------------------------------------------------------
//関数名：SHIORI3FW.SetDelayEvent/GetDelayEvent
//機能　：「遅れて喋る」イベントの登録/取得
//引数　：_argv[0] イベント _argv[1] 遅れsec.
//------------------------------------------------------------------------------

SHIORI3FW.SetDelayEvent : void
{
	if STRLEN(_argv[0]) != 0 && _argv[1] {
		SHIORI3FW.DelayEventTime = TOINT(_argv[1])
		SHIORI3FW.DelayEvent = _argv[0]
		SHIORI3FW.DelayEventReference = _argv[2,100] //範囲外は削られるのでこれでOK
	}
	else {
		SHIORI3FW.DelayEventTime = 0
		SHIORI3FW.DelayEvent = ''
		SHIORI3FW.DelayEventReference = IARRAY
	}
}

SHIORI3FW.GetDelayEvent
{
	(SHIORI3FW.DelayEvent,SHIORI3FW.DelayEventTime,SHIORI3FW.DelayEventReference)
}

//------------------------------------------------------------------------------
//関数名：SHIORI3FW.MakeReferenceAndPassThruInsVariable
//機能　：Reference*ヘッダからreference*変数/PassThru_Ins.*変数を作成します
//------------------------------------------------------------------------------
SHIORI3FW.MakeReferenceAndPassThruInsVariable : void {
	SHIORI3FW.ReferenceList = IARRAY
	SHIORI3FW.PassThru_Ins_List = IARRAY
	reference = IARRAY
	reference.raw = IARRAY
	_sz = ARRAYSIZE(var.req.key)

	for _i = 0; _i < _sz; _i++ {
		if SUBSTR(_keyname = var.req.key[_i], 0, 9) == 'Reference' {
			_refnum = TOINT(SUBSTR(_keyname, 9, 3))
			_value = var.req.value[_i]
			reference[_refnum] = _value
			reference.raw[_refnum] = var.req.rawvalue[_i]

			if SHIORI3FW.REF_ACCEL==0 {//1にすると高速化
				_varname = 'reference'+_refnum
				LETTONAME(_varname, _value)
				SHIORI3FW.ReferenceList ,= _varname
			}
		}
		elseif SUBSTR(_keyname = var.req.key[_i], 0, 16) == 'X-SSTP-PassThru-' {
			_PassThru_Ins_Name = SUBSTR(_keyname, 9, STRLEN(_keyname))
			_value = var.req.value[_i]

			_varname = 'PassThru_Ins.'+_PassThru_Ins_Name
			LETTONAME(_varname, _value)
			SHIORI3FW.PassThru_Ins_List ,= _varname
		}
	}
}

//------------------------------------------------------------------------------
//関数名：SHIORI3FW.ClearReferenceAndPassThruInsVariable
//機能　：reference*&PassThru_Ins.*変数を削除します
//------------------------------------------------------------------------------
SHIORI3FW.ClearReferenceAndPassThruInsVariable : void {
	foreach SHIORI3FW.ReferenceList; _refname { ERASEVAR(_refname) }
	ERASEVAR('SHIORI3FW.ReferenceList')
	ERASEVAR('reference','reference.raw')
	foreach SHIORI3FW.PassThru_Ins_List; _refname { ERASEVAR(_refname) }
	ERASEVAR('SHIORI3FW.PassThru_Ins_List')
}

//******************************************************************************
//チェイン制御
//******************************************************************************
//------------------------------------------------------------------------------
//関数名：SHIORI3FW.InitChain
//機能　：初期化
//------------------------------------------------------------------------------
SHIORI3FW.InitChain
{
	CHAIN.IDName = ''
	CHAIN.Script = ''
	CHAIN.Index  = 0
}

//------------------------------------------------------------------------------
//関数名：SHIORI3FW.StartChainTalk
//機能　：チェイン情報を解析してチェイン処理を開始します
//引数　：_argv[0] イベントID
//------------------------------------------------------------------------------
SHIORI3FW.StartChainTalk
{
	// チェイン名が無いなら何もしない
	if _argv[0] == ''; return

	// チェイン強制終了処理
	if _argv[0] == 'end' {
		SHIORI3FW.EndChainTalk
	}
	// 新たにチェイン開始
	else {
		CHAIN.IDName = _argv[0]
		CHAIN.Script = ''
		CHAIN.Index  = 0
		CHAIN.StartTime = GETSECCOUNT()
	}
}

//------------------------------------------------------------------------------
//関数名：SHIORI3FW.StartChainTalkScriptDirect
//機能　：直接次のチェインのスクリプトを指定します
//引数　：_argv[0] スクリプト
//------------------------------------------------------------------------------
SHIORI3FW.StartChainTalkScriptDirect
{
	// チェイン強制終了処理
	if _argv[0] == '' {
		SHIORI3FW.EndChainTalk
	}
	// 新たにチェイン開始
	else {
		CHAIN.IDName = ''
		CHAIN.Script = _argv[0]
		CHAIN.Index  = 0
		CHAIN.StartTime = GETSECCOUNT()
	}
}

//------------------------------------------------------------------------------
//関数名：SHIORI3FW.IsChain
//機能　：チェイン中かどうかを返します
//------------------------------------------------------------------------------
SHIORI3FW.IsChain
{
	if CHAIN.Script != '' {
		1
		return
	}
	if CHAIN.IDName != '' {
		1
		return
	}
	0
	return
}

//------------------------------------------------------------------------------
//関数名：ChainTalk
//機能　：チェイン処理　SHIORI3EV.OnAITalkNewEventから実行されます
//------------------------------------------------------------------------------
ChainTalk
{
	if CHAIN.IDName != '' {
		_res = EVAL(CHAIN.IDName)
		if _res != '' {
			_res
			return
		}
	}
	
	if CHAIN.Script != '' {
		_res = CHAIN.Script
		CHAIN.Script = ''
		_res
		return
	}
	
	SHIORI3FW.EndChainTalk
	RandomTalk
}

//------------------------------------------------------------------------------
//関数名：SHIORI3FW.EndChainTalk
//機能　：チェインを終了します
//------------------------------------------------------------------------------
SHIORI3FW.EndChainTalk
{
	CHAIN.IDName = ''
	CHAIN.Script = ''
	CHAIN.StartTime = 0
}

//------------------------------------------------------------------------------
//関数名：SHIORI3FW.ChainTime
//機能　：チェイン中の時間を取得します
//------------------------------------------------------------------------------
SHIORI3FW.ChainTime
{
	if CHAIN.StartTime {
		GETSECCOUNT() - TOINT(CHAIN.StartTime)
	}
	else {
		0
	}
}

//------------------------------------------------------------------------------
//関数名：SHIORI3FW.ClearChainVariable
//機能　：チェイン制御用の変数を消去します
//------------------------------------------------------------------------------
SHIORI3FW.ClearChainVariable
{
	ERASEVAR('CHAIN.IDName')
	ERASEVAR('CHAIN.Index' )
	ERASEVAR('CHAIN.Script' )
	ERASEVAR('CHAIN.StartTime' )
}

//******************************************************************************
//ネットワーク更新後にできた dl2 ファイルに関する処置
//******************************************************************************
//------------------------------------------------------------------------------
//関数名：SHIORI3FW.Dl2ToDll
//機能　：処理の開始
//------------------------------------------------------------------------------
SHIORI3FW.Dl2ToDll
{
	SHIORI3FW.ExecuteDl2ToDll('')
}

//------------------------------------------------------------------------------
//関数名：SHIORI3FW.ExecuteDl2ToDll
//機能　：指定したディレクトリに存在するdl2を処理
//引数　：_argv[0] 処理対象パス（AYA dllからの相対パス指定）
//------------------------------------------------------------------------------
SHIORI3FW.ExecuteDl2ToDll
{
	// 指定ディレクトリ内のファイルを列挙
	_files   = TOLOWER(FENUM(_argv[0]))
	// 拡張子dl2のファイルをリストに蓄積する　子ディレクトリ内も再帰的に処理する
	_targets = IARRAY
	foreach _files; _file {
		if SUBSTR(_file, 0, 1) == '\'
			SHIORI3FW.ExecuteDl2ToDll(_argv[0] + _file)
		else {
			_s_path = SPLITPATH(_file)
			if _s_path[3] == '.dl2'
				_targets ,= _s_path[2]
		}
	}

	// 拡張子dl2をdllへ名前変更する　旧dllは消去する
	foreach _targets; _target {
		_path = _argv[0] + '\' + _target
		void FDEL(_path + '.dll')
		void FRENAME(_path + '.dl2', _path + '.dll')
	}
}

//******************************************************************************
//インストール済ゴーストリストの構築
//******************************************************************************
//------------------------------------------------------------------------------
//関数名：On_installedghostname
//機能　：インストール済ゴースト名通知イベント
//------------------------------------------------------------------------------

On_installedghostname
{
	installedghostlist  = IARRAY
	installedsakuralist = IARRAY
	installedkerolist   = IARRAY

	// materia、もしくはイベントからの取得が指示されていなければファイル走査で取得する処理を実行
	if basewarename == 'embryo' || !SHIORI3FW.IGLIST_ACCEL {
		SHIORI3FW.StructInstalledGhostList
		return
	}

	// Referenceから取得
	installedghostlist = reference.raw
}

//------------------------------------------------------------------------------
//関数名：SHIORI3FW.StructInstalledGhostList
//機能　：構築
//------------------------------------------------------------------------------
SHIORI3FW.StructInstalledGhostList
{
	if !SHIORI3FW.IGLIST_MAX; return

	// ghostディレクトリ配下のディレクトリを抽出
	_filelist = FENUM('..\..\..')
	_dirlist  = IARRAY
	foreach _filelist; _file {
		if '\' _in_ _file; _dirlist ,= _file
	}
	_dirnum = ARRAYSIZE(_dirlist)

	// 取得開始位置と取得数を求める
	_num = 0
	if SHIORI3FW.IGLIST_MAX == -1 {
		StructGhostList.Index = 0
		_num                  = _dirnum
	}
	else {
		if GETTYPE(StructGhostList.Index) != 1 || StructGhostList.Index >= _dirnum
			StructGhostList.Index = 0
		_num = SHIORI3FW.IGLIST_MAX
		if _num > _dirnum; _num = _dirnum
	}

	// リスト作成主処理
	for _i = 0; _i < _num; _i++ {
		// 取得位置の更新
		StructGhostList.Index++
		
		if StructGhostList.Index >= _dirnum; StructGhostList.Index = 0

		// descript.txtから情報取得
		_ghostname = SHIORI3FW.GetGhostNameFromDescriptTxt(/
						'..\..\..' + _dirlist[StructGhostList.Index])
		// リストへ追加
		if ARRAYSIZE(_ghostname) {
			installedghostlist  ,= _ghostname[0]
			installedsakuralist ,= _ghostname[1]
			installedkerolist   ,= _ghostname[2]
		}
	}
}

//------------------------------------------------------------------------------
//関数名：SHIORI3FW.GetGhostNameFromDescriptTxt
//機能　：descript.txtからname/sakura.name/kero.nameを取得します
//引数　：_argv[0] 取得対象のゴーストディレクトリ
//------------------------------------------------------------------------------
SHIORI3FW.GetGhostNameFromDescriptTxt
{
	_charset = SHIORI3FW.GetCharsetFromDescriptTxt(_argv[0])
	if _charset == ''
		_charset = 'Shift_JIS'
	FCHARSET(_charset)
	if !FOPEN(_filename = "%_argv[0]\ghost\master\descript.txt", "r")
		return

	_ghostname = IARRAY
	_flag      = 0
	while _flag != 7 {
		if (_line = FREAD(_filename)) == -1; break
		
		case CUTSPACE(_line[0]) {
			when 'name' {
				_ghostname[0] = CUTSPACE(_line[1])
				_flag++
			}
			when 'sakura.name' {
				_ghostname[1] = CUTSPACE(_line[1])
				_flag += 2
			}
			when 'kero.name' {
				_ghostname[2] = CUTSPACE(_line[1])
				_flag += 4
			}
		}
	}

	FCLOSE(_filename)
	_ghostname
}

//------------------------------------------------------------------------------
//関数名：SHIORI3FW.GetCharsetFromDescriptTxt
//機能　：descript.txtからcharsetを取得します
//引数　：_argv[0] 取得対象のゴーストディレクトリ
//------------------------------------------------------------------------------
SHIORI3FW.GetCharsetFromDescriptTxt
{
	if !FOPEN(_filename = "%_argv[0]\ghost\master\descript.txt", "r")
		return

	_charset = ''
	_flag = 0
	while _flag != 1 {
		if (_line = FREAD(_filename)) == -1; break
		
		case TOLOWER(CUTSPACE(_line[0])) {
			when 'charset' {
				_charset = CUTSPACE(_line[1])
				_flag++
			}
		}
	}

	FCLOSE(_filename)
	_charset
}

//******************************************************************************
//文 version 3 システム関数 / システム変数の補完
//FUNCTIONEX / SAORI
//******************************************************************************
//------------------------------------------------------------------------------
//関数名：FUNCTIONLOAD
//機能　：SAORIを読み込みます
//引数　：_argv[0]　 対象DLL名
//------------------------------------------------------------------------------
FUNCTIONLOAD
{
	// load　エラーなら抜ける　初回のloadならGET Versionを送出する
	_r_load = LOADLIB(_argv[0])
	if !_r_load {
		0
		return
	}
	if _r_load == 1 {
		if SUBSTR(SHIORI3FW.SendGETVersion(_argv[0]), 0, 1) != '2' {
			UNLOADLIB(_argv[0])
			0
			return
		}
		SAORI.DllList ,= _argv[0]
	}
	1
}

//------------------------------------------------------------------------------
//関数名：FUNCTIONEX
//機能　：SAORIを実行します
//引数　：_argv[0]　 対象DLL名
//　　　　_argv[1]～ Argument0～
//------------------------------------------------------------------------------
FUNCTIONEX
{
	if FUNCTIONLOAD(_argv[0]) == 0 {
		0
		return
	}

	// リクエスト文字列を作成
	_reqheader = /
		"EXECUTE SAORI/1.0%(C_CRLF)Charset: %(CHARSETLIBEX(_argv[0]))%(C_CRLF)Sender: AYA%(C_CRLF)SecurityLevel: Local%(C_CRLF)"
	for _i = 1; _i < _argc; _i++ {
		_reqheader += "Argument%(_i - 1): %(_argv[_i])%(C_CRLF)"
	}
	_reqheader += C_CRLF

	// 実行
	SHIORI3FW.RequestLib(_argv[0], _reqheader)

	if var.lib.result != ''
		var.lib.result
	SHIORI3FW.MakeValueexVariable
}

//------------------------------------------------------------------------------
//関数名：SAORI
//機能　：SAORIを実行します
//        FUNCTIONEXのシノニム。一段下駄が入る分FUNCTIONEXより低速になりますが、
//        気にするほどではありません。
//------------------------------------------------------------------------------
SAORI { FUNCTIONEX(_argv) }

//------------------------------------------------------------------------------
//関数名：SHIORI3FW.SendGETVersion
//機能　：SAORIへGET Versionを送出します
//引数　：_argv[0] SAORIファイル名
//------------------------------------------------------------------------------
SHIORI3FW.SendGETVersion
{
	SHIORI3FW.RequestLib(_argv[0], "GET Version SAORI/1.0%(C_CRLF)Charset: %(CHARSETLIBEX(_argv[0]))%(C_CRLF)Sender: AYA%(C_CRLF2)")

	LIB.STATUSCODE
}

//------------------------------------------------------------------------------
//関数名：SHIORI3FW.RequestLib
//機能　：SAORIへリクエストを送出し、結果を得ます
//引数　：_argv[0] SAORIファイル名
//　　　　_argv[1] リクエスト文字列
//------------------------------------------------------------------------------
SHIORI3FW.RequestLib
{
	LIB.PROTOCOL   = ''
	LIB.STATUSCODE = ''

	// リクエスト送出
	_result = REQUESTLIB(_argv[0], _argv[1])

	// 結果の解析

	// 改行で分割
	_lines = RE_SPLIT(_result, C_CRLF)

	// プロトコル名と結果の取得
	_seppos = STRSTR(_lines[0], ' ', 0)
	if _seppos == -1; return
	LIB.PROTOCOL   = SUBSTR(_lines[0], 0, _seppos)
	LIB.STATUSCODE = SUBSTR(_lines[0], _seppos + 1, STRLEN(_lines[0]) - _seppos - 1)

	// レスポンスヘッダの取得
	var.lib.key   = IARRAY
	var.lib.value = IARRAY
	var.lib.result = ''
	
	_skip = 1
	foreach _lines; _lin {
		// 1行目は飛ばす
		if _skip {
			_skip = 0
			continue
		}

		// キーと値を取得
		_len         = STRLEN(_lin)
		_pos         = STRSTR(_lin, ': ', 0)
		var.lib.key ,= (_key = SUBSTR(_lin, 0, _pos))
		_value       = SUBSTR(_lin, _pos + 2, _len - _pos - 2)
		_valuecv     = ''
		
		if var.lib.key == '' {
			break
		}

		if SHIORI3FW.AUTO_DATA_CONVERT {
			if ISINTSTR(_value)
				_valuecv = TOINT(_value)
			elseif ISREALSTR(_value)
				_valuecv = TOREAL(_value)
			else
				_valuecv = REPLACE(_value, C_BYTE1, ',')
		}
		else {
			_valuecv = TOAUTOEX(_value)
		}
		
		var.lib.value ,= _valuecv
		if _key == 'Result' {
			var.lib.result = _valuecv
		}
	}
}

//------------------------------------------------------------------------------
//関数名：SHIORI3FW.MakeValueexVariable
//機能　：リクエストヘッダからvalueex*変数を作成します
//引数　：_argv[0] SAORIファイル名
//　　　　_argv[1] リクエスト文字列
//------------------------------------------------------------------------------
SHIORI3FW.MakeValueexVariable
{
	SHIORI3FW.ValueexList = IARRAY
	_sz                   = ARRAYSIZE(var.lib.key)
	for _i = 0; _i < _sz; _i++ {
		if SUBSTR(_keyname = var.lib.key[_i], 0, 5) == "Value" {
			_varname = "valueex%(SUBSTR(_keyname, 5, 3))"
			LETTONAME(_varname, var.lib.value[_i])
			SHIORI3FW.ValueexList ,= _varname
			valueex[TOINT(SUBSTR(_keyname, 5, 3))] = var.lib.value[_i]
		}
	}
}

//------------------------------------------------------------------------------
//関数名：SHIORI3FW.ClearValueexVariable
//機能　：valueex*変数を削除します
//------------------------------------------------------------------------------
SHIORI3FW.ClearValueexVariable
{
	foreach SHIORI3FW.ValueexList; _exname { ERASEVAR(_exname) }
	ERASEVAR('SHIORI3FW.ValueexList')
	ERASEVAR('valueex')
}

//------------------------------------------------------------------------------
//関数名：SHIORI3FW.SaoriUnloadAll
//機能　：ロードされているSAORIをすべてunloadします
//------------------------------------------------------------------------------
SHIORI3FW.SaoriUnloadAll
{
	foreach SAORI.DllList; _saori { UNLOADLIB(_saori) }

	SAORI.DllList = IARRAY
}

//------------------------------------------------------------------------------
//関数名：SHIORI3FW.RegisterTempVar
//機能　：終了時に削除するグローバル変数を登録します。
//------------------------------------------------------------------------------
SHIORI3FW.RegisterTempVar
{
	for _i = 0 ; _i < _argc ; _i++ {
		if ASEARCH(_argv[_i],tempvarname) < 0 {
			tempvarname ,= _argv[_i]
		}
	}
}

//------------------------------------------------------------------------------
//　関数名：SHIORI3FW.RemoveAllTags
//　機能　：全てのさくらスクリプトタグを消去する。
//　引数　：argv0＝テキスト
//------------------------------------------------------------------------------
SHIORI3FW.RemoveAllTags
{
	_text = RE_REPLACE(_argv[0],'\\_{0,2}[a-zA-Z0-9*!&](\d|\[("([^"]|\\")+?"|([^\]]|\\\])+?)+?\])?','')
	_text = REPLACE(_text,'\\','\')
	_text;
}

//------------------------------------------------------------------------------
//　関数名：SHIORI3FW.CanTalk
//　機能　：Statusヘッダからしゃべれるかどうかを判定する
//------------------------------------------------------------------------------
SHIORI3FW.CanTalk
{
	if 'talking' _in_ status {
		0
		return
	}
	if 'choosing' _in_ status {
		0
		return
	}
	if 'minimizing' _in_ status {
		0
		return
	}
	if 'timecritical' _in_ status {
		0
		return
	}
	
	1
	return
}

//******************************************************************************
//配列操作
//******************************************************************************
JOIN
{
	if _argc <= 2 {
		_argv[0]
		return
	}
	
	_delim = _argv[_argc - 1]
	_argc--
	
	_text = _argv[0]
	for _i = 1 ; _i < _argc ; _i++ {
		_text += _delim
		_text += _argv[_i]
	}
	_text
}

REVERSE
{
	_a = IARRAY
	for _i = 0 ; _i < _argc ; _i++ {
		_a ,= _argv[_argc-_i-1]
	}
	_a
}

UNIQUE
{
	ARRAYDEDUP(_argv)
}

SPLITEX
{
	_a = SPLIT(_argv)
	_n = ARRAYSIZE(_a)
	for _i = _n-1 ; _i >= 0 ; _i-- {
		if _a[_i] == '' {
			_a[_i] = IARRAY
		}
	}
	_a
}

MAX
{
	_v = _argv[0]
	for _i = 1 ; _i < _argc ; _i++ {
		if _v < _argv[_i] {
			_v = _argv[_i]
		}
	}
	_v
}

MIN
{
	_v = _argv[0]
	for _i = 1 ; _i < _argc ; _i++ {
		if _v > _argv[_i] {
			_v = _argv[_i]
		}
	}
	_v
}

AVERAGE
{
	_v = 0
	for _i = 0 ; _i < _argc ; _i++ {
		_v += _argv[_i]
	}
	_v / _argc
}

//******************************************************************************
// システムで処理するイベント群
//******************************************************************************
SHIORI3EV.OnUpdateReady : void
{
	// OnUpdateReadyならロード中のSAORIをすべてunloadする
	SHIORI3FW.SaoriUnloadAll
}

SHIORI3EV.OnUpdateComplete : void
{
	// OnUpdateCompleteならdl2を探す。存在したら同名のdllを削除、dl2はdllにリネームする
	// もっともAYA自身が対象だった場合はどうしようもないが。
	SHIORI3FW.Dl2ToDll
}

SHIORI3EV.OnSurfaceChange : void
{
	if reference[2] != '' {
		_r2 = SPLIT(reference[2],',')
		_s = TOINT(_r2[1])
		_c = TOINT(_r2[0])
		
		if _s >= 0 {
			SHIORI3FW.LastSurface[_c] = _s
			SHIORI3FW.IsVisible[_c] = 1
		}
		else {
			SHIORI3FW.IsVisible[_c] = 0
		}
	}
	else {
		_s0 = TOINT(reference[0])
		_s1 = TOINT(reference[1])
		
		if _s0 >= 0 {
			SHIORI3FW.LastSurface[0] = _s0
			SHIORI3FW.IsVisible[0] = 1
		}
		else {
			SHIORI3FW.IsVisible[0] = 0
		}
		
		if _s1 >= 0 {
			SHIORI3FW.LastSurface[1] = _s1
			SHIORI3FW.IsVisible[1] = 1
		}
		else {
			SHIORI3FW.IsVisible[1] = 0
		}
	}
}

SHIORI3EV.On_hwnd : void
{
	SHIORI3FW.HWnd = RE_SPLIT(reference[0],'[\x1,]')
	SHIORI3FW.BalloonHWnd = RE_SPLIT(reference[1],'[\x1,]')
	
	_m = ARRAYSIZE(SHIORI3FW.HWndOld)
	_n = ARRAYSIZE(SHIORI3FW.HWnd)
	
	if _n < _m {
		_n = _m
	}
	
	for _i = 0 ; _i < _n ; _i++ {
		SHIORI3FW.HWnd[_i] = TOINT(SHIORI3FW.HWnd[_i])
		SHIORI3FW.HWndOld[_i] = TOINT(SHIORI3FW.HWndOld[_i])
	}
	
	_refold  = reference
	_refold0 = reference0
	
	for _i = 0 ; _i < _n ; _i++ {
		if SHIORI3FW.HWndOld[_i] != SHIORI3FW.HWnd[_i] {
			reference[0] = _i
			reference0 = _i
			
			if SHIORI3FW.HWndOld[_i] == 0 {
				void EVAL('OnSHIORI3FW.WindowCreate')
			}
			elseif SHIORI3FW.HWnd[_i] == 0 {
				void EVAL('OnSHIORI3FW.WindowDestroy')
			}

			SHIORI3FW.HWndOld[_i] = SHIORI3FW.HWnd[_i]
		}
	}
	
	reference = _refold
	reference0 = _refold0
}

SHIORI3EV.On_uniqueid : void
{
	SHIORI3FW.UniqueID = reference[0]
}

SHIORI3EV.On_capability : void
{
	SHIORI3FW.Capability = reference
}

SHIORI3EV.OnNotifySelfInfo : void
{
	SHIORI3FW.GhostName = reference[0]
	SHIORI3FW.ShellNameOld = SHIORI3FW.ShellName
	SHIORI3FW.ShellName = reference[3]
	SHIORI3FW.ShellPath = reference[4]
	SHIORI3FW.BalloonNameOld = SHIORI3FW.BalloonName
	SHIORI3FW.BalloonName = reference[5]
	SHIORI3FW.BalloonPath = reference[6]
	
	selfname = reference[1]
	sakuraname = reference[1]
	keroname = reference[2]
	
	EVAL('OnSHIORI3FW.ChangeSelfInfo')
}

SHIORI3EV.OnNotifyUserInfo : void
{
	SHIORI3FW.UserName = reference[0]
	SHIORI3FW.UserNameFull = reference[1]
	SHIORI3FW.UserBirthday = SPLIT(reference[2],',',3)
	for _i = 0 ; _i < 3 ; _i++ {
		SHIORI3FW.UserBirthday[_i] = TOINT(SHIORI3FW.UserBirthday[_i])
	}
	SHIORI3FW.UserSex = reference[3]
}

SHIORI3EV.OnShellChanged : void
{
	SHIORI3FW.ShellName = reference[0]
	SHIORI3FW.ShellPath = reference[2]
	
	EVAL('OnSHIORI3FW.ChangeSelfInfo')
}

SHIORI3EV.OnBalloonChange : void
{
	SHIORI3FW.BalloonName = reference[0]
	SHIORI3FW.BalloonPath = reference[1]
	
	EVAL('OnSHIORI3FW.ChangeSelfInfo')
}

//------------------------------------------------------------------------------
//OnAITalkNewEvent
//------------------------------------------------------------------------------
SHIORI3EV.OnAITalkNewEvent
{
	//互換用処理
	_old_name = SHIORI3FW.TranslateEvent('OnAiTalk')
	if ISFUNC(_old_name) {
		EVAL(_old_name)
		return
	}
	_old_name = SHIORI3FW.TranslateEvent('OnAITalk')
	if ISFUNC(_old_name) {
		EVAL(_old_name)
		return
	}
	
	//---- 通常のランダムトーク、ただしチェイン中はチェイントーク
	if SHIORI3FW.IsChain {
		ChainTalk
	}
	else {
		RandomTalk
	}
}

//---- SHIORI 関連情報 ------------------------------------------------------------------
SHIORI3EV.On_version
{
	GETSETTING('coreinfo.version')
}

SHIORI3EV.On_craftman
{
	GETSETTING('coreinfo.author')
}

SHIORI3EV.On_craftmanw
{
	GETSETTING('coreinfo.author')
}

SHIORI3EV.On_name
{
	GETSETTING('coreinfo.name')
}

//******************************************************************************
// デバッグ
//******************************************************************************
SHIORI3EV.On_enable_log : void
{
	if TOINT(reference[0]) {
		SETSETTING('log','yaya_ssp_debug.log')
	}
	else {
		SETSETTING('log','')
	}
}

SHIORI3EV.On_log_path : void
{
	SHIORI3FW.Path + 'yaya_ssp_debug.log'
}

SHIORI3EV.On_enable_debug : void
{
	SHIORI3FW.DebugMode = TOINT(reference[0])
}

SHIORI3EV.On_ShioriEcho
{
	if SHIORI3FW.DebugMode {
		'\0Eval Result = ' + JOIN(EVAL(reference[0]),',')
	}
}

DUMP : void
{
	void FOPEN('dump.txt','ab')
	void FWRITE('dump.txt',JOIN(_argv,',') + C_CRLF)
	void FCLOSE('dump.txt')
}

//******************************************************************************
// 時刻系変数
//******************************************************************************
year    { GETTIME[0] }
month   { GETTIME[1] }
day     { GETTIME[2] }
weekday { GETTIME[3] }
hour    { GETTIME[4] }
ampm    { if hour >= 12; 1;   else; 0    }
hour12  { if ampm; hour - 12; else; hour }
minute  { GETTIME[5] }
second  { GETTIME[6] }

systemuptickcount { GETTICKCOUNT }
systemuptime
{
	_highcount = GETTICKCOUNT(1)
	if _highcount > 1
		GETTICKCOUNT/1000
	else
		(_highcount*0x40000000 + (GETTICKCOUNT/2))/500
}

systemupsecond    { SHIORI3FW.GetTickInfo[3] }
systemupminute    { SHIORI3FW.GetTickInfo[1] }
systemuphour      { SHIORI3FW.GetTickInfo[2] }

SHIORI3FW.GetTickInfo
{
	_result     = (IARRAY, systemuptime)
	_result    ,= _result[0]/60
	_result    ,= _result[1]/60
	_result    ,= _result[0] - _result[1]*60
	_result[1] -= _result[2]*60

	_result
}

ghostupmin
{
	(GETSECCOUNT() / 60) - ghostbootmin
}

ghostupmin_total
{
	ghostupmin_last + ghostupmin()
}

//******************************************************************************
// メモリ系変数
//******************************************************************************
memoryload         { GETMEMINFO[0] }
memorytotalphys    { GETMEMINFO[1] }
memoryavailphys    { GETMEMINFO[2] }
memorytotalvirtual { GETMEMINFO[3] }
memoryavailvirtual { GETMEMINFO[4] }

