//******************************************************************************
/*
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('communicateratio') == 0 {
		communicateratio = COM_RATIO
	}
	if ISVAR('ghostupmin_last') == 0 {
		ghostupmin_last = 0
	}
	if ISVAR('ghostuptimes') == 0 {
		ghostuptimes = 0
	}
	ghostuptimes += 1

	// 毎回起動時の初期化
	SHIORI3FW.ResetAITalkInterval(-1)

	_sec = GETSECCOUNT

	SHIORI3FW.SaveVarCount = 0
	SHIORI3FW.LastTalk     = ''
	SHIORI3FW.LastTalkTime = _sec
	SHIORI3FW.LastAITalk   = ''
	SHIORI3FW.LastAITalkTime = _sec
	SHIORI3FW.TalkEndTime  = _sec

	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    = 0
	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
	ghostbootsec           = GETSECCOUNT

	S_CHARSET = GETSETTING('charset.output')

	status = ''
	SHIORI3FW.tempvarname = IARRAY

	array_eventtranslate = ASORT('string,ascending',GETFUNCLIST('OnGhostEventTranslate'))
	array_translateinternal = ASORT('string,ascending',GETFUNCLIST('OnTranslateInternal'))

	installedghostlist = IARRAY
	installedsakuralist = IARRAY
	installedkerolist = IARRAY

	SHIORI3FW.BasewareNotifysInstalledSakuraName = 0
	SHIORI3FW.BasewareNotifysInstalledKeroName = 0

	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
selfname
sakuraname
keroname
basewarename
basewarenameex
sender
S_CHARSET
installedghostlist
installedsakuralist
installedkerolist
ascii
uniqueid
ghostbootmin
ghostbootsec
status
res_securitylevel
res_reference
res_marker
marker
res_valuenotify
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
SHIORI3FW.DebugMode
SHIORI3FW.Status
SHIORI3FW.UserName
SHIORI3FW.UserNameFull
SHIORI3FW.UserBirthday
SHIORI3FW.UserSex
SHIORI3FW.BasewareNotifysInstalledSakuraName
SHIORI3FW.BasewareNotifysInstalledKeroName
SHIORI3FW.LastSurface
SHIORI3FW.IsVisible
C_CRLF2
C_CRLF
C_BYTE1
'>>
	//以下は保存
	//SHIORI3FW.ShellName
	//SHIORI3FW.ShellNameOld
	//SHIORI3FW.ShellPath
	//SHIORI3FW.BalloonName
	//SHIORI3FW.BalloonNameOld
	//SHIORI3FW.BalloonPath

	ERASEVAR(SPLIT(_var,C_CRLF))

	if ARRAYSIZE(SHIORI3FW.tempvarname) {
		ERASEVAR(SHIORI3FW.tempvarname)
	}
	ERASEVAR('SHIORI3FW.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

	status = ''
	SHIORI3FW.Eventid=''
	SHIORI3FW.SecurityLevel='local'//互換性のためデフォルトはlocal

	while _lineend > _linestart {
		//行分割
		_linestart = _lineend + 2
		_lineend = STRSTR(_reqdata, C_CRLF, _linestart)

		//空行もしくはみつからなかった
		if _lineend <= _linestart {
			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
			}
		}
		// セキュリティレベル
		elseif _key == 'SecurityLevel' {
			SHIORI3FW.SecurityLevel = _value
		}
		// ベースウェア名取得
		elseif _key == 'Sender' {
			if basewarenameex == '' {
				basewarenameex = _value
			}
			basewarename = _value
			sender = _value
		}
		//Status
		elseif _key == 'Status' {
			status = _value
		}

		// キーと値を記憶
		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)
		}
	}

	if SHIORI3FW.Eventid == 'OnAiTalk' || SHIORI3FW.Eventid == 'OnAITalk' {
		SHIORI3FW.Eventid = 'OnAITalkNewEvent'
	}

	if TOLOWER(SHIORI3FW.SecurityLevel) == 'external' {
		SHIORI3FW.Eventid = 'ExternalEvent.' + SHIORI3FW.Eventid
	}

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

	// ハンドラが無い場合は即返る
	if !ISFUNC(SHIORI3FW.EventidTranslate) && !SHIORI3FW.IsImportantEvent {
		SHIORI3FW.MakeEmptyResponse(SHIORI3FW.Eventid)
		--
		C_CRLF

		return
	}

	OnRequest
}

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.PushErrorAlart(SHIORI3FW.ErrorLevel,SHIORI3FW.ErrorDescription)
			}
		}

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

	--
	SHIORI3FW.GetAdditionalReturns
	--
	C_CRLF
}

//------------------------------------------------------------------------------
//関数名：SHIORI3FW.PushErrorAlart
//機能　：Push Error Alart
//------------------------------------------------------------------------------
SHIORI3FW.PushErrorAlart{
	SHIORI3FW.PushAdditionalReturn('ErrorLevel',_argv[0])
	SHIORI3FW.PushAdditionalReturn('ErrorDescription',_argv[1])
}


//------------------------------------------------------------------------------
//関数名：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])
	_argv[0] = IARRAY
	SHIORI3FW.PushAdditionalReturn(_h,_argv)
}

//------------------------------------------------------------------------------
//関数名：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_securitylevel != '' {
			_result += "SecurityLevel: %(res_securitylevel)%(C_CRLF)"
			ERASEVAR('res_securitylevel')
		}
	}

	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
	_event_fallback = _event

	_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 == '' {
		if _event != _event_fallback {
			if ISFUNC(_event_fallback) {
				_result = TOSTR(EVAL(_event_fallback))
			}
		}
	}
	if _result == '' {
		_result = _result_internal
	}

	if _result != '' { //トークあり
		// 遅延EVAL
		_talk=IARRAY
		if SHIORI3FW.ENABLE_DELAYED_EVAL
			_talk = RE_SPLIT(_result,'(' + EVAL_DELIM + '|' + CHAIN_DELIM + ')')
		else
			_talk = RE_SPLIT(_result,'(' + 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 SHIORI3FW.ENABLE_DELAYED_EVAL && _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(STRLEN(_talk[0]))
			}
			if 'Surface' !_in_ _event {
				if SHIORI3FW.RemoveAllTags(_talk[0]) != '' {
					SHIORI3FW.LastTalkTime = GETSECCOUNT
				}
			}
			_talk[0]
		}
	}
	else { //トークなし
		if _event == 'OnAITalkNewEvent' {
			SHIORI3FW.LastAITalk = ''
			SHIORI3FW.ResetAITalkInterval(0)
		}
	}

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

//------------------------------------------------------------------------------
//関数名：SHIORI3FW.MakeEmptyResponse
//機能　：204を返してはいけないリクエストを判定して、
//        適正な応答文字列を作成します
//引数　：_argv[0] イベント名
//------------------------------------------------------------------------------
SHIORI3FW.MakeEmptyResponse
{
	case _argv[0] {
		when 'OnFirstBoot', 'OnGhostChanged' {
			_evt = SHIORI3FW.TranslateEvent('OnBoot')
			if ISFUNC(_evt) {
				//SSP側でフォールバックするのでここでは204を返す
				"SHIORI/3.0 204 No Content%(C_CRLF)Charset: %(S_CHARSET)%(C_CRLF)"
			}
			else {
				// OnBootハンドラもない時に、スコープ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 'OnBoot', 'OnWindowStateRestore' {
			// スコープ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トーク発動処理カウンタのリセット
//引数　：_argv[0] スクリプトの長さ もしくは初期化時-1
//------------------------------------------------------------------------------
SHIORI3FW.ResetAITalkInterval
{
	SHIORI3FW.LastAITalkTime = GETSECCOUNT
	if _argv[0] != 0 {
		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/sakuraname/keroname
//機能　：インストール済ゴースト名通知イベント
//------------------------------------------------------------------------------
On_installedsakuraname {
	SHIORI3FW.BasewareNotifysInstalledSakuraName=1
	
	installedsakuralist = reference.raw
}

On_installedkeroname {
	SHIORI3FW.BasewareNotifysInstalledKeroName=1
	
	installedkerolist = reference.raw
}

On_installedghostname {
	//Referenceから取得
	installedghostlist = reference.raw
	
	if SHIORI3FW.BasewareNotifysInstalledSakuraName && SHIORI3FW.BasewareNotifysInstalledKeroName {
		return
	}
	

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

		SHIORI3FW.StructInstalledGhostList
	}
}

//------------------------------------------------------------------------------
//関数名：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
		}
		// 相手のCharsetに合わせる
		_i = 0
		foreach var.lib.key; _key {
			if _key == 'Charset' {
				void CHARSETLIBEX(_argv[0], var.lib.value[_i])
				break
			}
			_i++
		}
		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*変数を作成します
//------------------------------------------------------------------------------
SHIORI3FW.MakeValueexVariable
{
	SHIORI3FW.ClearValueexVariable
	valueex = 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])
			valueex[TOINT(SUBSTR(_keyname, 5, 3))] = var.lib.value[_i]
		}
	}
}

//------------------------------------------------------------------------------
//関数名：SHIORI3FW.ClearValueexVariable
//機能　：valueex*変数を削除します
//------------------------------------------------------------------------------
SHIORI3FW.ClearValueexVariable
{
	_valueexList = GETVARLIST('valueex')
	foreach _valueexList; _exname {
		if RE_MATCH(_exname, 'valueex[0-9]+') {
			ERASEVAR(_exname)
		}
	}
	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],SHIORI3FW.tempvarname) < 0 {
			SHIORI3FW.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.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
{
	if RAND(100) < communicateratio
	{
		//---- communicateratio % の率で他ゴーストへ話しかけ
		if ISFUNC('StartCommunicate') {
			StartCommunicate
			return
		}
		//else: ?
	}
	//互換用処理
	_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')
}

//---- tama対応 ------------------------------------------------------------------
SHIORI3EV.On_tamaOpen
{
	SHIORI3FW.TamaHwnd = reference[0]
	SETTAMAHWND(SHIORI3FW.TamaHwnd)
}

SHIORI3EV.On_tamaExit
{
	ERASEVAR('SHIORI3FW.TamaHwnd')
	SETTAMAHWND(0)
}

SHIORI3EV.OnInitialize
{
	if reference[0] == 'reload' {
		SETTAMAHWND(SHIORI3FW.TamaHwnd)
	}
}

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

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

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

SHIORI3EV.On_ShioriEcho
{
	if ISFUNC('On_ShioriEcho') {//User costom ShioriEcho
		EVAL('On_ShioriEcho')
	}
	elseif SHIORI3FW.DebugMode {
		_result=EVAL(reference[0])
		'\0Eval Result = ' + JOIN(_result,',')
		SHIORI3FW.Push_X_SSTP_PassThru('Result',_result)
		SHIORI3FW.Push_X_SSTP_PassThru('Type','id: '+GETTYPEEX('_result'))
	}
	else {
		'\0Need DebugMode'
		SHIORI3FW.Push_X_SSTP_PassThru('Special','Need DebugMode')
	}
}

SHIORI3EV.On_Has_Event : void {
	_event_name=reference.raw[0]
	_SecurityLevel=reference.raw[1]
	if !_SecurityLevel
		_SecurityLevel=SHIORI3FW.SecurityLevel
	if SUBSTR(_event_name,0,2) != 'On'
		_event_name='On_'+_event_name
	_result=0
	if TOLOWER(_SecurityLevel) == 'external'
		_event_name='ExternalEvent.'+_event_name
	_result=ISFUNC(_event_name)
	if !_result
		_result=ISFUNC('SHIORI3EV.'+_event_name)
	SHIORI3FW.Push_X_SSTP_PassThru('Result',_result)
}
SHIORI3EV.ExternalEvent.On_Has_Event{
	SHIORI3EV.On_Has_Event
}
SHIORI3EV.On_Get_Supported_Events: void {
	_L=GETFUNCLIST('On')
	_base_local_event_funcs=IARRAY
	foreach _L;_func{
		if SUBSTR(_func,2,1) == '_'
			_func=SUBSTR(_func,3,STRLEN(_func))
		_base_local_event_funcs,=_func
	}
	_L=GETFUNCLIST('SHIORI3EV.On')
	foreach _L;_func{
		_func=SUBSTR(_func,10,STRLEN(_func))
		if SUBSTR(_func,2,1) == '_'
			_func=SUBSTR(_func,3,STRLEN(_func))
		_base_local_event_funcs,=_func
	}
	SHIORI3FW.Push_X_SSTP_PassThru('local',ARRAYDEDUP(_base_local_event_funcs))
	_L=GETFUNCLIST('ExternalEvent.On')
	_base_external_event_funcs=IARRAY
	foreach _L;_func{
		_func=SUBSTR(_func,14,STRLEN(_func))
		if SUBSTR(_func,2,1) == '_'
			_func=SUBSTR(_func,3,STRLEN(_func))
		_base_external_event_funcs,=_func
	}
	_L=GETFUNCLIST('SHIORI3EV.ExternalEvent.On')
	foreach _L;_func{
		_func=SUBSTR(_func,24,STRLEN(_func))
		if SUBSTR(_func,2,1) == '_'
			_func=SUBSTR(_func,3,STRLEN(_func))
		_base_external_event_funcs,=_func
	}
	SHIORI3FW.Push_X_SSTP_PassThru('external',ARRAYDEDUP(_base_external_event_funcs))
}
SHIORI3EV.ExternalEvent.On_Get_Supported_Events{
	SHIORI3EV.On_Get_Supported_Events
}

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	{ hour / 12 }
hour12	{ hour % 12 }
hour12ex{ if hour12 == 0; 12 ; else ; hour12 }
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
{
	if ISVAR('ghostbootmin') {
		(GETSECCOUNT / 60) - ghostbootmin
	}
	else {
		0
	}
}

ghostupmin_total
{
	ghostupmin_last + ghostupmin
}

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

