int SaoriClient::request( const vector<string>& i_argument, bool i_is_secure, string& o_result, vector<string>& o_value) { //--------------------- // リクエスト作成 strpairvec data; int idx=0; for ( vector<string>::const_iterator i=i_argument.begin() ; i!=i_argument.end() ; ++i,++idx ) { data.push_back( strpair(string("Argument")+itos(idx), *i) ); } data.push_back( strpair("SecurityLevel", (i_is_secure ? "Local" : "External") ) ); //--------------------- // リクエスト実行 strpairvec r_data; int return_code = this->SakuraDLLClient::request("EXECUTE", data, r_data); //--------------------- // 返答を解析 string result; for ( strpairvec::const_iterator i = r_data.begin() ; i != r_data.end() ; ++i ) { const string& key = i->first; const string& value = i->second; if ( compare_head(key, "Value") && isdigit(key[strlen("Value")]) ) { const int pos = atoi(key.c_str() + strlen("Value")); if ( pos<0 || pos>65536 ) { continue; } if ( o_value.size() <= pos ) { o_value.resize(pos+1); } o_value[pos] = value; } else if ( key=="Result" ) { o_result = value; } } return return_code; }
int Satori::request( const string& i_protocol, const string& i_protocol_version, const string& i_command, const strpairvec& i_data, string& o_protocol, string& o_protocol_version, strpairvec& o_data) { SenderEnableBuffering seb(GetSender()); //------------------------------------------------- // リクエスト単位のクラスメンバを初期化 mRequestMap.clear(); mRequestID = ""; mReferences.clear(); mReferences.reserve(8); // 最小値 // 引数をクラスメンバに設定 mRequestCommand = i_command; mRequestType = i_protocol; mRequestVersion = i_protocol_version; mStatusLine = i_command + " " + i_protocol + "/" + i_protocol_version; // 返すプロトコルのデフォルト値 o_protocol = "SHIORI"; o_protocol_version = "3.0"; // 喋るごとに初期化する変数 return_empty = false; surface_restore_at_talk_onetime = SR_INVALID; auto_anchor_enable_onetime = auto_anchor_enable; is_quick_section = false; // スクリプトヘッダ header_script = ""; // プロトコルを判別 if ( i_protocol=="SAORI" && i_protocol_version[0]>='1' ) { o_protocol = i_protocol; o_protocol_version = "1.0"; mRequestMode = SAORI; } else if ( i_protocol=="SHIORI" && i_protocol_version[0]>='3' ) { mRequestMode = SHIORI3; } else if ( i_protocol=="SHIORI" && i_protocol_version[0]=='2' ) { mRequestMode = SHIORI2; // 2.xにもバージョンとしては3.0を返す } else if ( i_protocol=="MAKOTO" && i_protocol_version[0]>='2' ) { o_protocol = i_protocol; o_protocol_version = "2.0"; mRequestMode = MAKOTO2; } else if ( i_protocol=="UNKNOWN" ) { mRequestMode = UNKNOWN; } else { // 未対応のプロトコルだった。 return 400; } // データ部をmRequestMapに格納。 // SHOIRI/3.0以外のプロトコルの場合、SHOIRI/3.0に変換を行う。 for ( strpairvec::const_iterator it = i_data.begin() ; it != i_data.end() ; ++it ) { string key = it->first; const string& value = it->second; switch ( mRequestMode ) { case SAORI: if ( compare_head(key, "Argument") ) { int n = stoi_internal(key.c_str()+8); if ( n==0 ) key = "ID"; else key = "Reference" + itos(n-1); } break; case SHIORI2: // こっちはてきとー if ( key=="Event" ) key="ID"; break; case MAKOTO2: if ( key=="String" ) key="Reference0"; break; default: break; } mRequestMap[key] = value; if ( compare_head(key, "Reference") ) { int n = stoi_internal(key.c_str()+9); if ( n>=0 && n<65536 ) { if ( n>=mReferences.size() ) mReferences.resize(n+1); mReferences[n]=value; } } } if ( mRequestMode == MAKOTO2 ) { mRequestMap["ID"] = "OnMakoto"; } mRequestID = mRequestMap["ID"]; mIsMateria = ( mRequestMap["Sender"] == "embryo" ); mIsStatusHeaderExist = ( mRequestMap["Sender"] == "SSP" ); //------------------------------------------------- // リクエストを解釈 if ( mRequestCommand=="GET Version" ) { if ( mRequestMode == SHIORI2 ) { o_data.push_back( strpair("ID", gSatoriName) ); o_data.push_back( strpair("Craftman", gSatoriCraftman) ); o_data.push_back( strpair("Version", gSatoriVersion) ); } return 200; } // 選択分岐記録を変数に代入。ref0を元に戻す。 if ( compare_head(mRequestID, "OnChoice") ) // OnChoiceSelect/OnChoiceEnterの両方 { strvec vec; int ref_no = ( mRequestID=="OnChoiceEnter" || mRequestID=="OnChoiceSelectEx" )?1:0; string& info = mRequestMap[string("Reference")+itos(ref_no)]; if ( split(info, byte1_dlmt, vec)==3 ) // \1区切りの3文字列であるならば { info=mReferences[ref_no]=variables["選択ID"]=vec[0]; variables["選択ラベル"]=vec[1]; variables["選択番号"]=vec[2]; } } // ログについて色々 bool log_disable_soft = ( mRequestID=="OnSurfaceChange" || mRequestID=="OnSecondChange" || mRequestID=="OnMinuteChange" || mRequestID=="OnMouseMove" || mRequestID=="OnTranslate"); bool log_disable_hard = ( /*compare_tail(mRequestID, "caption") || */compare_tail(mRequestID, "visible") || compare_head(mRequestID, "menu.") || mRequestID.find(".color.")!=string::npos ); GetSender().next_event(); if(fRequestLog) { GetSender().sender() << "--- Request ---" << std::endl << mStatusLine <<std::endl; // << iRequest <<std::endl; for(strmap::const_iterator i=mRequestMap.begin() ; i!=mRequestMap.end() ; ++i) if ( !i->first.empty() && !i->second.empty() && i->first!="SecurityLevel" && i->first!="Sender" && i->first!="Charset" ) { GetSender().sender() << i->first << ": " << i->second <<std::endl; } } // せきゅあ? strmap::const_iterator it = mRequestMap.find("SecurityLevel"); secure_flag = ( it!=mRequestMap.end() && stricmp(it->second.c_str(), "local")==0 ); // メイン処理 GetSender().sender() << "--- Operation ---" << std::endl; int status_code = 500; if ( mRequestID=="enable_log" || mRequestID=="enable_debug" ) { if ( secure_flag ) { bool flag = false; if ( mReferences.size() > 0 ) { flag = atoi(mReferences[0].c_str()) != 0; } if ( mRequestID=="enable_debug" ) { fDebugMode = flag; } else { GetSender().validate(flag); } status_code = 200; } else { GetSender().sender() << "local/Localでないので蹴りました: ShioriEcho" <<std::endl; status_code = 403; } } else if ( mRequestID=="ShioriEcho" ) { // ShioriEcho実装 if ( fDebugMode && secure_flag ) { string result = SentenceToSakuraScriptExec_with_PreProcess(mReferences); if ( result.length() ) { static const char* const dangerous_tag[] = {"\\![updatebymyself]", "\\![vanishbymyself]", "\\![enter,passivemode]", "\\![enter,inductionmode]", "\\![leave,passivemode]", "\\![leave,inductionmode]", "\\![lock,repaint]", "\\![unlock,repaint]", "\\![biff]", "\\![open,browser", "\\![open,mailer", "\\![raise", "\\j["}; std::string replace_to; for ( int i = 0 ; i < (sizeof(dangerous_tag)/sizeof(dangerous_tag[0])) ; ++i ) { replace_to = "¥["; replace_to += dangerous_tag[i]+2; //\をヌキ replace(result,dangerous_tag[i],replace_to); } //Translate(result); - Translateは後でかかる mResponseMap["Value"] = result; status_code = 200; } else { status_code = 204; } } else { if ( fDebugMode ) { GetSender().sender() << "local/Localでないので蹴りました: ShioriEcho" <<std::endl; status_code = 403; } else { static const std::string dbgmsg = "デバッグモードが無効です。使用するためには$デバッグ=有効にしてください。: ShioriEcho"; GetSender().sender() << dbgmsg <<std::endl; mResponseMap["Value"] = "\\0" + dbgmsg + "\\e"; status_code = 200; } } } else if (mRequestID == "SatolistEcho"){ #ifndef POSIX // さとりすとデバッガ実装 if (fDebugMode && secure_flag) { //R0は除去される strvec customRef; for (int i = 1; i < mReferences.size(); i++){ customRef.push_back(mReferences[i]); } string result = SentenceToSakuraScriptExec_with_PreProcess(customRef); if (result.length()) { result = string("SSTP 200 OK\r\nCharset: Shift_JIS\r\nResult: ") + result + "\r\n\r\n"; } else{ //情報なし result = string("SSTP 204 No Content\r\nCharset: Shift_JIS\r\n\r\n"); } char* copyData = new char[result.length() + 1]; strcpy(copyData, result.c_str()); COPYDATASTRUCT cds; cds.dwData = 0; cds.cbData = result.length() + 1; cds.lpData = copyData; DWORD ret; SendMessageTimeout((HWND)stoi_internal(mReferences[0]), WM_COPYDATA, (WPARAM)NULL, (LPARAM)&cds, 0, 1000, &ret); //ここで204を返すと非対応時のエラーを出すので200で通知メッセージを表示する status_code = 200; mResponseMap["Value"] = "\\0\\_q■情報を送信しました。\\e"; } else { if (fDebugMode) { GetSender().sender() << "local/Localでないので蹴りました: SatolistEcho" <<std::endl; status_code = 403; } else { static const std::string dbgmsg = "デバッグモードが無効です。使用するためには$デバッグ=有効にしてください。: SatolistEcho"; GetSender().sender() << dbgmsg <<std::endl; mResponseMap["Value"] = "\\0" + dbgmsg + "\\e"; status_code = 200; } } #endif // POSIX } else { status_code = CreateResponse(mResponseMap); } // 後処理1 default_surface = next_default_surface; //-------------------------------------------------------------------- // Valueに対する最終処理 if ( status_code==200 ) { // && compare_head(mRequestID, "On") strmap::iterator i = mResponseMap.find("Value"); if ( i!=mResponseMap.end() ) { if ( return_empty ) { status_code = 204; mResponseMap.erase(i); } else { if ( !Translate(i->second) ) { status_code = 204; mResponseMap.erase(i); } else { second_from_last_talk = 0; if ( compare_head(mRequestID, "On") ) { mResponseHistory.push_front(i->second); if ( mResponseHistory.size() >= RESPONSE_HISTORY_SIZE ) mResponseHistory.pop_back(); } } } } } GetSender().sender() << "status code : " << itos(status_code) <<std::endl; //-------------------------------------------------------------------- for(strmap::const_iterator i=mResponseMap.begin() ; i!=mResponseMap.end() ; ++i) { string key=i->first, value=i->second; switch ( mRequestMode ) { case SAORI: if ( key=="Value" ) { key = "Result"; value = header_script + value; } else if ( compare_head(key, "Reference") ) { key = string() + "Value" + (key.c_str()+9); } break; case SHIORI2: if ( key=="Value" ) { key = "Sentence"; value = header_script + value; } break; case MAKOTO2: if ( key=="Value" ) { key = "String"; value = header_script + value; } break; default: if ( key=="Value" ) { value = header_script + value; } break; } o_data.push_back( strpair(key, value) ); } if ( GetSender().errsender().get_log_mode() ) { const std::vector<string> &errlog = GetSender().errsender().get_log(); std::string errmsg; std::string errlevel; for ( std::vector<string>::const_iterator itr = errlog.begin() ; itr != errlog.end(); ++itr ) { errmsg += "SATORI - "; errmsg += mRequestID; errmsg += " > "; errmsg += *itr; errmsg += "\1"; errlevel += "critical\1"; } if ( errmsg.length() ) { errmsg.erase(errmsg.end()-1,errmsg.end()); errlevel.erase(errlevel.end()-1,errlevel.end()); o_data.push_back( strpair("ErrorLevel",errlevel) ); o_data.push_back( strpair("ErrorDescription",errmsg) ); } GetSender().errsender().clear_log(); } GetSender().validate(); if(fResponseLog) { GetSender().sender() << "--- Response ---" <<std::endl << mResponseMap <<std::endl; } mResponseMap.clear(); if ( log_disable_hard ) { GetSender().delete_last_request(); } else if ( log_disable_soft ) { if ( status_code != 200 ) { GetSender().delete_last_request(); } } GetSender().flush(); //-------------------------------------------------------------------- // リロード処理 if ( reload_flag ) { reload_flag = false; string tmp = mBaseFolder; GetSender().sender() << "■■reloading." <<std::endl; unload(); load(tmp); GetSender().sender() << "■■reloaded." <<std::endl; GetSender().flush(); } return status_code; }
int Satori::CreateResponse(strmap& oResponse) { // NOTIFYであれば値を保存 /*strmap mNotifiedMap; if ( mRequestCommand=="NOTIFY" ) mNotifiedMap[mRequestID] = mRequestMap["Reference0"];*/ if ( mRequestCommand=="NOTIFY" ) { if ( mRequestID=="hwnd" ) { #ifndef POSIX strvec vec; const int max = split(mReferences[0], byte1_dlmt, vec); if ( max > 0 ) { characters_hwnd.clear(); } for (int n=0 ; n<max ; ++n) { characters_hwnd[n] = (HWND)(stoi(vec[n])); sender << "里々は id:" << n << " のhWndを取得しました。" << endl; } #endif } else if ( mRequestID=="capability" ) { bool isErrorHeader = false; for ( strvec::const_iterator it = mReferences.begin() ; it != mReferences.end(); ++it ) { if ( *it == "response.errorlevel" ) { isErrorHeader = true; } } errsender.set_log_mode(isErrorHeader); } } string result; //喋り変換部初期化 reset_speaked_status(); //実際の呼び出し開始 if ( mRequestID == "OnDirectSaoriCall" ) { string str; int n=0; bool isfirst = true; for (strvec::const_iterator i=mReferences.begin() ; i!=mReferences.end() ; ++i, ++n) if ( i->empty() && mRequestMap.find( string("Reference")+itos(n) )==mRequestMap.end() ) break; else if ( isfirst ) { str = *i; isfirst = false; } else str += "," + *i; //while ( compare_tail(str, ",") ) // str.assign( str.substr(0, str.size()-2)+"]" ); string temp; if ( secure_flag ) { sender << "[DirectCall]" << endl; Call(str, temp); return 204; } else { sender << "local/Localでないので蹴りました: " << str << endl; return 204; } } else if ( compare_head(mRequestID, "On") ) { if ( words.is_exist(mRequestID) ) return Call(mRequestID, oResponse["Value"]) ? 200 : 204; return EventOperation(mRequestID, oResponse); } else if ( mRequestID == "version" ) result = gSatoriVersion; else if ( mRequestID == "craftman" ) result = gSatoriCraftman; else if ( mRequestID == "craftmanw" ) result = gSatoriCraftmanW; else if ( mRequestID == "name" ) result = gSatoriName; else if ( mRequestID.find(".recommendsites") != string::npos || mRequestID=="sakura.portalsites" ) { if ( !GetURLList(mRequestID, result) ) // URLリストの取得 return 204; } else if ( !Call(mRequestID, result) ) { return 204; } else { } if ( result.empty() ) return 204; oResponse["Value"] = result; return 200; }
bool Satori::Translate(string& ioScript) { if ( ioScript.empty() ) return false; const bool is_OnTranslate = (mRequestID=="OnTranslate"); // さくらスクリプトとそれ以外を分割して処理を加える vector<string> vec; string acum; bool content=false; // 文に中身があるのか bool is_first_question = true; // 選択分岐記録の消去処理用。 int last_speaker=0; const char* p = ioScript.c_str(); while (*p) { string c=get_a_chr(p); // 全角半角問わず一文字取得し、pを一文字すすめる if ( c=="\\" || c=="%" ) { if (*p=='\\'||*p=='%') { // エスケープされた\, % acum += c + *p++; continue; } const char* start=p; string cmd="",opt=""; while (!_ismbblead(*p) && (isalpha(*p)||isdigit(*p)||*p=='!'||*p=='*'||*p=='&'||*p=='?'||*p=='_')) ++p; cmd.assign(start, p-start); if (*p=='[') { const char* opt_start = ++p; while (*p!=']') { if (p[0]=='\\' && p[1]==']') // エスケープされた] ++p; p += _ismbblead(*p) ? 2 : 1; } opt.assign(opt_start, p++ -opt_start); } // 選択分岐ラベルに対する特殊処理 if ( !is_OnTranslate && cmd=="q" && opt!="" && count(opt, ",")>0 && mRequestID!="OnHeadlinesense.OnFind") { // 選択分岐があるスクリプトであれば、その初回で選択分岐記録をクリア if ( is_first_question ) { question_record.clear(); is_first_question = false; } // 選択分岐を記録 { strvec vec; split(opt, ",", vec); if ( vec.size()==1 ) // countとsplitの罠。 vec.push_back(""); // string label=vec[0], id=vec[1]; if ( false == compare_head(id, "On") ) { // Onで始まるものはOnChoiceSelectを経由されないため、対象外とする int count = question_record.size()+1; question_record[id] = pair<int,string>(count, label); // ラベルIDに書き戻し opt = label+","+id+byte1_dlmt+label+byte1_dlmt+itos(count); for (int i=2;i<vec.size();++i) opt += string(",") + vec[i]; } } } else if ( cmd=="0" || cmd=="h" ) { last_speaker=0; } else if ( cmd=="1" || cmd=="u" ) { last_speaker=1; } else if ( cmd=="p" && aredigits(opt) ) { last_speaker=stoi(opt); if ( mIsMateria || last_speaker<=1 ) { cmd = (opt=="0") ? "0" : "1"; opt = ""; } } else if ( cmd=="s" && !opt.empty() ) { last_talk_exiting_surface[last_speaker]=stoi(opt); } else if ( cmd.size()==2 && cmd[0]=='s' && isdigit(cmd[1]) ) { last_talk_exiting_surface[last_speaker]=cmd[1]-'0'; } if ( !acum.empty() ) vec.push_back(acum); if ( opt=="" ) vec.push_back(c + cmd); else vec.push_back(c + cmd + "[" + opt + "]"); acum=""; static set<string> nc_cmd; // 有効と数えないさくらスクリプト群 static bool initialized=false; if (!initialized) { initialized=true; nc_cmd.insert("0"); nc_cmd.insert("1"); nc_cmd.insert("h"); nc_cmd.insert("u"); nc_cmd.insert("p"); nc_cmd.insert("n"); nc_cmd.insert("w"); nc_cmd.insert("_w"); nc_cmd.insert("e"); } if ( nc_cmd.find(cmd)==nc_cmd.end() ) content = true; } else { content = true; acum += c; } } if ( !acum.empty() ) vec.push_back(acum); if (!content) return false; // 中身の無いスクリプト(実行してもしなくても一緒)と判断。 ioScript=""; for (vector<string>::iterator i=vec.begin() ; i!=vec.end() ; ++i) { if ( i->at(0)!='\\' && i->at(0)!='%' ) { // さくらスクリプト以外の文への処理 // アンカー挿入 if ( !is_OnTranslate ) for ( set<string>::iterator j=anchors.begin() ; j!=anchors.end() ; ++j ) replace(*i, *j, string("\\_a[")+*j+"]"+*j+"\\_a"); } ioScript += *i; } // 事後置き換え辞書を適用 if ( !is_OnTranslate ) for ( strmap::iterator di=replace_after_dic.begin() ; di!=replace_after_dic.end() ; ++di ) replace(ioScript, di->first, di->second); diet_script(ioScript); // ラストダイエット // エスケープしてあった文字を戻す m_escaper.unescape(ioScript); return true; }
bool Satori::system_variable_operation(string key, string value, string* result) { // mapにしようよ。 if ( key == "喋り間隔" ) { talk_interval = zen2int(value); if ( talk_interval<3 ) talk_interval=0; // 3未満は喋らない // 喋りカウント初期化 int dist = static_cast<int>(talk_interval*(talk_interval_random/100.0)); talk_interval_count = ( dist==0 ) ? talk_interval : (talk_interval-dist)+(random(dist*2)); return true; } if ( key == "喋り間隔誤差" ) { talk_interval_random = zen2int(value); if ( talk_interval_random>100 ) talk_interval_random=100; if ( talk_interval_random<0 ) talk_interval_random=0; // 喋りカウント初期化 int dist = int(talk_interval*(talk_interval_random/100.0)); talk_interval_count = ( dist==0 ) ? talk_interval : (talk_interval-dist)+(random(dist*2)); return true; } if ( key =="見切れてても喋る" ) { is_call_ontalk_at_mikire= (value=="有効"); return true; } if ( key == "今回は喋らない" ) { return_empty=(value=="有効"); return true; } if ( key == "スクリプトの一番頭" ) { header_script = value; return true; } if ( key == "呼び出し回数制限" ) { m_nest_limit = zen2int(value); if ( m_nest_limit < 0 ) { m_nest_limit = 0; } return true; } if ( key == "ジャンプ回数制限" ) { m_jump_limit = zen2int(value); if ( m_jump_limit < 0 ) { m_jump_limit = 0; } return true; } if ( key == "スコープ切り換え時" ) { append_at_scope_change = zen2han(value); return true; } if ( key == "さくらスクリプトによるスコープ切り換え時" ) { append_at_scope_change_with_sakura_script = zen2han(value); return true; } if ( key == "トーク開始時" ) { append_at_talk_start = zen2han(value); return true; } if ( key == "トーク終了時" ) { append_at_talk_end = zen2han(value); return true; } if ( key == "選択肢開始時" ) { append_at_choice_start = zen2han(value); return true; } if ( key == "選択肢終了時" ) { append_at_choice_end = zen2han(value); return true; } if ( key == "会話時サーフェス戻し" || key == "会話時サーフィス戻し" ) { if ( value == "有効" ) { surface_restore_at_talk = SR_NORMAL; } else if ( value == "強制" ) { surface_restore_at_talk = SR_FORCE; } else { surface_restore_at_talk = SR_NONE; } return true; } if ( key == "今回は会話時サーフェス戻し" || key == "今回は会話時サーフィス戻し" ) { if ( value == "有効" ) { surface_restore_at_talk_onetime = SR_NORMAL; } else if ( value == "強制" ) { surface_restore_at_talk_onetime = SR_FORCE; } else { surface_restore_at_talk_onetime = SR_NONE; } return true; } if ( key == "自動アンカー" ) { if ( value == "有効" ) { auto_anchor_enable = true; auto_anchor_enable_onetime = auto_anchor_enable; } else { auto_anchor_enable = false; auto_anchor_enable_onetime = auto_anchor_enable; } return true; } if ( key == "今回は自動アンカー" ) { if ( value == "有効" ) { auto_anchor_enable_onetime = true; } else { auto_anchor_enable_onetime = false; } return true; } if ( compare_head(key, "サーフェス加算値") && aredigits(key.c_str() + const_strlen("サーフェス加算値")) ) { int n = zen2int(key.c_str() + const_strlen("サーフェス加算値")); surface_add_value[n]= zen2int(value); variables[string()+"デフォルトサーフェス"+itos(n)] = value; next_default_surface[n] = zen2int(value); if ( !is_speaked_anybody() ) default_surface[n]=next_default_surface[n]; return true; } if ( compare_head(key, "デフォルトサーフェス") && aredigits(key.c_str() + const_strlen("デフォルトサーフェス")) ) { int n = zen2int(key.c_str() + const_strlen("デフォルトサーフェス")); next_default_surface[n]= zen2int(value); if ( !is_speaked_anybody() ) default_surface[n]=next_default_surface[n]; return true; } if ( compare_head(key, "BalloonOffset") && aredigits(key.c_str() + const_strlen("BalloonOffset")) ) { int n = stoi(key.c_str() + const_strlen("BalloonOffset")); BalloonOffset[n] = value; validBalloonOffset[n] = true; return true; } if ( key == "トーク中のなでられ反応") { insert_nade_talk_at_other_talk= (value=="有効"); return true; } if ( key == "なでられ持続秒数") { nade_valid_time_initializer = zen2int(value); return true; } if ( key == "なでられ反応回数") { nade_sensitivity = zen2int(value); return true; } if ( key == "デバッグ" ) { fDebugMode = (value=="有効"); return true; } if ( key == "Log" ) { Sender::validate(value=="有効"); return true; } if ( key == "RequestLog" ) { fRequestLog = (value=="有効"); return true; } if ( key == "OperationLog" ) { fOperationLog = (value=="有効"); return true; } if ( key == "ResponseLog" ) { fResponseLog = (value=="有効"); return true; } if ( key == "自動挿入ウェイトの倍率" ) { rate_of_auto_insert_wait= zen2int(value); rate_of_auto_insert_wait = min(1000, max(0, rate_of_auto_insert_wait)); variables["自動挿入ウェイトの倍率"] = int2zen(rate_of_auto_insert_wait); return true; } if ( key == "自動挿入ウェイトタイプ" ) { if ( value == "一般" ) { type_of_auto_insert_wait = 2; variables["自動挿入ウェイトタイプ"] = "一般"; } else if ( value == "無効" ) { type_of_auto_insert_wait = 0; variables["自動挿入ウェイトタイプ"] = "無効"; } else /* if ( value == "里々" ) */ { type_of_auto_insert_wait = 1; variables["自動挿入ウェイトタイプ"] = "里々"; } return true; } if ( key == "辞書フォルダ" ) { strvec words; split(value, ",",dic_folder); reload_flag=true; return true; } if ( key == "セーブデータ暗号化" ) { fEncodeSavedata = (value=="有効"); return true; } if ( compare_head(key,"単語群「") && compare_tail(key,"」の重複回避") ) { variables.erase(key); words.setOC( string(key.c_str()+8, key.length()-8-12), value ); return true; } if ( compare_head(key,"文「") && compare_tail(key,"」の重複回避") ) { variables.erase(key); talks.setOC( string(key.c_str()+4, key.length()-4-12), value ); return true; } if ( key == "次のトーク" ) { variables.erase(key); int count=1; while ( reserved_talk.find(count) != reserved_talk.end() ) ++count; reserved_talk[count] = value; sender << "次回のランダムトークが「" << value << "」に予\x96\xf1されました。" << endl; return true; } if ( compare_head(key,"次から") && compare_tail(key,"回目のトーク") ) { variables.erase(key); int count = zen2int( string(key.c_str()+6, key.length()-6-12) ); if ( count<=0 ) { sender << "トーク予\x96\xf1、設定値がヘンです。" << endl; } else { while ( reserved_talk.find(count) != reserved_talk.end() ) ++count; reserved_talk[count] = value; sender << count << "回後のランダムトークが「" << value << "」に予\x96\xf1されました。" << endl; } return true; } if ( key=="トーク予\x96\xf1のキャンセル" ) { if ( value=="*" ) reserved_talk.clear(); else for (map<int, string>::iterator it=reserved_talk.begin(); it!=reserved_talk.end() ; ) if ( value == it->second ) reserved_talk.erase(it++); else ++it; return true; } if ( key == "SAORI引数の計算" ) { if (value=="有効") mSaoriArgumentCalcMode = SACM_ON; else if (value=="無効") mSaoriArgumentCalcMode = SACM_OFF; else mSaoriArgumentCalcMode = SACM_AUTO; return true; } if ( key == "辞書リロード" && value=="実行") { variables.erase(key); reload_flag=true; return true; } if ( key == "手動セーブ" && value=="実行") { variables.erase(key); if ( is_dic_loaded ) { this->Save(); } return true; } if ( key == "自動セーブ間隔" ) { mAutoSaveInterval = zen2int(value); mAutoSaveCurrentCount = mAutoSaveInterval; if ( mAutoSaveInterval > 0 ) sender << "" << itos(mAutoSaveInterval) << "秒間隔で自動セーブを行います。" << endl; else sender << "自動セーブは行いません。" << endl; return true; } if ( key == "全タイマ解除" && value=="実行") { variables.erase(key); for (strintmap::const_iterator i=timer.begin();i!=timer.end();++i) variables.erase(i->first + "タイマ"); timer.clear(); return true; } if ( key == "教わること" ) { variables.erase(key); teach_genre=value; if ( result != NULL ) *result += "\\![open,teachbox]"; return true; } if ( key.size()>6 && compare_tail(key, "タイマ") ) { string name(key.c_str(), strlen(key.c_str())-6); /*if ( sentences.find(name) == sentences.end() ) { result = string("※ タイマ終了時のジャンプ先 *")+name+" がありません ※"; // セーブデータ復帰時を考慮 } else */{ int sec = zen2int(value); if ( sec < 1 ) { variables.erase(key); if ( timer.find(name)!=timer.end() ) { timer.erase(name); sender << "タイマ「" << name << "」の予\x96\xf1がキャンセルされました。" << endl; } else sender << "タイマ「" << name << "」は元から予\x96\xf1されていません。" << endl; } else { timer[name] = sec; sender << "タイマ「" << name << "」が" << sec << "秒後に予\x96\xf1されました。" << endl; } } return true; } if ( key == "引数区切り追加" && value.size()>0 ) { variables.erase(key); mDelimiters.insert(value); return true; } if ( key == "引数区切り削除" && value.size()>0 ) { variables.erase(key); mDelimiters.erase(value); return true; } if ( compare_head(key, "Value") && aredigits(key.c_str() + 5) ) { variables.erase(key); if(value!=""){ mResponseMap[string()+"Reference"+key.substr(5)] = value; }else{ mResponseMap.erase(string()+"Reference"+key.substr(5)); } return true; } return false; }
int Satori::SentenceToSakuraScriptInternal(const strvec &vec,string &result,string &jump_to,ptrdiff_t &ip) { // 再帰管理 static int nest_count=0; ++nest_count; //DBG(sender << "enter SentenceToSakuraScriptInternal, nest-count: " << nest_count << ", vector_size: " << vec.size() << endl); if ( m_nest_limit > 0 && nest_count > m_nest_limit ) { sender << "呼び出し回数超過" << endl; --nest_count; return 0; } static const int basewait=3; strvec::const_iterator it = vec.begin(); std::advance(it,ip); for ( ; it != vec.end() ; ++it) { const char* p = it->c_str(); //DBG(sender << nest_count << " '" << p << "'" << endl); if ( it==vec.begin() && strncmp(p, "→", 2)==0 ) { p+=2; updateGhostsInfo(); // ゴースト情報を更新 if ( ghosts_info.size()>=2 ) { // そもそも自分以外にゴーストはいるのか。 string temp = p; vector<strmap>::iterator i=ghosts_info.begin(); ++i; // 自分は飛ばす for ( ; i!=ghosts_info.end() ; ++i ) { string name = (*i)["name"]; sender << "ghost: " << name <<endl; if ( compare_head(temp, name) ) {// 相手を特定 mCommunicateFor = name; p += mCommunicateFor.size(); break; } } if ( i==ghosts_info.end() ) { // 特定しなかった場合 // ランダム //int n = random(ghosts_info.size()-1))+1; //assert( n>=1 && n < ghosts_info.size()); mCommunicateFor = (ghosts_info[1])["name"]; // あかん、隣で起動している〜〜にならん } } } // 選択肢 \q?[id,string] if ( strncmp(p, "_", 2)==0 ) { if ( strlen(p)>1023 ) continue; char buf[1024]; strncpy(buf, p+2, sizeof(buf) / sizeof(buf[0])); char* choiced = buf; char* id = (char*)strstr_hz(buf, "\t"); // 選択肢ラベルとジャンプ先の区切り result += append_at_choice_start; if ( id == NULL ) { string str=UnKakko(choiced); //result += string("\\q")+itos(question_num++)+"["+str+"]["+str+"]"; result += "\\q["+str+","+str+"]"; } else { *id++='\0'; while ( *id=='\t' ) ++id; // 選択肢ラベルとジャンプ先の区切り //result += string("\\q")+itos(question_num++)+"["+UnKakko(id)+"]["+UnKakko(choiced)+"]"; result += "\\q["+UnKakko(choiced)+","+UnKakko(id)+"]"; } result += append_at_choice_end; continue; } // ちょっと微妙な存在意義。 if ( strncmp(p, "\\s", 2)==0 ) { if ( !is_speaked(speaker) ) { if ( surface_changed_before_speak.find(speaker) == surface_changed_before_speak.end() ) { surface_changed_before_speak.insert(map<int,bool>::value_type(speaker,is_speaked_anybody()) ); } } } // ジャンプ if ( strncmp(p, ">", 2)==0 || strncmp(p, "≫", 2)==0 ) { strvec words; split(p+2, "\t", words, 2); // ジャンプ先とジャンプ条件の区切り if ( words.size()>=2 ) { string r; if ( !calculate(words[1], r) ) break; if ( zen2int(r) == 0 ) { sender << "*計算結果が0だったため、続行します。" << endl; continue; } } if ( words.size() >= 1 ) { jump_to = UnKakko(words[0].c_str(),false,true); } else { jump_to.erase(); } if ( strncmp(p, "≫", 2)==0 ) { ip = std::distance(vec.begin(),it) + 1; --nest_count; return 2; } else { ip = std::distance(vec.begin(),it) + 1; --nest_count; return 1; } } // 変数を設定 if ( strncmp(p, "$", 2)==0 ) { const char* v; string value; bool do_calc=false; p+=2; if ( (v=strstr_hz(p, "\t"))!=NULL ) { // 変数名と変数に設定する内容の区切り value = UnKakko(v+1,false,true); } else if ( (v=strstr_hz(p, "="))!=NULL || (v=strstr_hz(p, "="))!=NULL ) { value = UnKakko(v+((*v=='=') ? 1 : 2),false,true); do_calc=true; } else { //v = p+strlen(p); //value=""; result += "\\n※ $による変数代入文には、タブによる区切りか、=による計算式が必要です ※\\n\\n[half]'" + value +"'"; break; } string key(p, v-p); key = UnKakko(key.c_str(),false,true); if ( key=="" ) { result += "$"; // $そのまま表示 speaked_speaker.insert(speaker); } else if ( aredigits(zen2han(key)) ) { sender << "$" << key << " 数字のみの変数名は扱えません." << endl; erase_var(key); // 存在抹消 } else if ( value=="" ) { sender << "$" << key << "/cleared." << endl; erase_var(key); // 存在抹消 system_variable_operation(key, "", &result);//存在抹消したものがシステム変数かも! } else { bool isOverwritten; bool isSysValue; // "0"は代入先を先に参照する時、エラーを返さないように。 string *pstr = GetValue(key,isSysValue,true,&isOverwritten,"0"); if ( do_calc ) { if ( !calculate(value, value) ) break; if ( aredigits(value) ) { value = int2zen(stoi(value)); } } sender << "$" << key << "=" << value << "/" << (isOverwritten ? "written." : "overwritten.")<< endl; if ( pstr ) { *pstr = value; } system_variable_operation(key, value, &result); } continue; } // 通常処理 while ( p[0] != '\0' ) { string c=get_a_chr(p); // 全角半角問わず一文字取得し、pを一文字すすめる if ( c=="(" ) { // 何かを取得・挿入 character_wait_exec; result += KakkoSection(p); } else if ( c=="\xff" ) { //内部特殊表現 c=get_a_chr(p); if ( c=="\x01" ) { //0xff0x01=スコープ切り替え 後に半角数値が1文字続く c=get_a_chr(p); int speaker_tmp = stoi(c.c_str()); if ( is_speaked(speaker) && speaker != speaker_tmp ) { result += append_at_scope_change; chars_spoken += 2; } speaker = speaker_tmp; character_wait_clear(2); if ( speaker == 0 ) { result += "\\0"; } else if ( speaker == 1 ) { result += "\\1"; } else { result += "\\p[" + c + "]"; } } } else if ( c==":" ) { // スコープ切り替え - ここは二人を想定。 if ( is_speaked(speaker) ) { result += append_at_scope_change; chars_spoken += 2; } speaker = (speaker==0) ? 1 : 0; character_wait_clear(2); result += (speaker ? "\\1" : "\\0"); } else if ( c=="\\" || c=="%" ) { // さくらスクリプトの解釈、というか解釈のスキップ。 if (*p=='\\'||*p=='%') { // エスケープされた\, % result += c + *p++; continue; } const char* start=p; string cmd="",opt=""; while (!_ismbblead(*p) && (isalpha(*p)||isdigit(*p)||*p=='!'||*p=='-'||*p=='*'||*p=='&'||*p=='?'||*p=='_')) ++p; cmd.assign(start, p-start); if ( cmd == "_?" || cmd == "_!" ) { //エスケープ処理 この間に自動タグ挿入はしない const char* e1 = strstr(p,"\\_?"); if ( ! e1 ) { e1 = strstr(p,"\\_!"); } if ( e1 ) { character_wait_exec; result += c; result += cmd; opt.assign(p,e1-p+3); opt = UnKakko(opt.c_str()); p = e1 + 3; //endtag result += opt; continue; } } if (*p=='[') { const char* opt_start = ++p; while (*p!=']') { if (p[0]=='\\' && p[1]==']') // エスケープされた] ++p; p += _ismbblead(*p) ? 2 : 1; } opt.assign(opt_start, p++ -opt_start); opt=UnKakko(opt.c_str()); } if ( cmd=="n" ) { // 改行 character_wait_clear(2); } else if ( ( (cmd=="0" || cmd=="h") && speaker==1) || ( (cmd=="1" || cmd=="u") && speaker==0) ) { // スコープ切り替え if ( is_speaked(speaker) ) { result += append_at_scope_change_with_sakura_script; chars_spoken += 2; } speaker = stoi(cmd); character_wait_clear(2); } else if ( cmd=="p" && aredigits(opt) ) { int spktmp = stoi(opt); if ( speaker != spktmp ) { // スコープ切り替えonSSP/CROW if ( is_speaked(speaker) ) { result += append_at_scope_change_with_sakura_script; chars_spoken += 2; } speaker = spktmp; character_wait_clear(2); } } else if ( cmd=="s" ) { //サーフィス切り替えの前にウェイトは済ませておくこと character_wait_exec; } else if ( cmd=="_q" ) { if ( ! is_quick_section ) { //これからクイックセクションなのでウエイトを全部消化 character_wait_exec; } is_quick_section = ! is_quick_section; } if ( opt!="" ) { //sender << "ss_cmd: " << c << "," << cmd << "," << opt << endl; result += c + cmd + "[" + opt + "]"; } else { //sender << "ss_cmd: " << c << "," << cmd << endl; result += c + cmd; } //result += string(start, p-start); } else { // 通常の一文字 character_wait_exec; speaked_speaker.insert(speaker); result += c; chars_spoken += c.size(); if ( c=="。" || c=="、" ) { character_wait_clear(c=="、"?1:2); } } } character_wait_clear(2); result += "\\n"; } //DBG(sender << "leave SentenceToSakuraScriptInternal, nest-count: " << nest_count << endl); --nest_count; return 0; }
int main( int argc, char *argv[ ], char *envp[ ] ) { // 実行ファイルのあるフォルダ名を取得 TCHAR szPath[MAX_PATH]=""; ::GetModuleFileName(NULL, szPath, MAX_PATH); char* p = FindFinalChar(szPath, '\\'); if ( *p == NULL ) return 1; *p='\0'; base_folder = szPath; sender << "[sodate]" << endl; sender << "ディスク上の対象フォルダは " << base_folder << "です。" << endl; ::SetCurrentDirectory(base_folder.c_str()); // 設置場所の確認 if ( !isExistFolder( (base_folder+"\\ghost\\master").c_str() ) ) { error("ghost/masterが見つかりません。設置位置を確認してください。"); return false; } if ( !isExistFolder( (base_folder+"\\shell\\master").c_str() ) ) { error("shell/masterが見つかりません。設置位置を確認してください。"); return false; } // 設定ファイルの読み込み sender << "設定ファイル sodate.dat を読み込みます。" << endl; if ( !strmap_from_file(conf, base_folder+"\\sodate.dat", byte_value_1) ) { error("先に sodate_setup.exe を実行してください。"); return false; } // 暗号保存されたパスワードをデコード string str = decode(conf["password"]); int len = str.size()/2; byte* buf = new byte[len+1]; buf[len]='\0'; string_to_binary(str, buf); xor_filter(buf, len, 186); conf["password"] = decode( (char*)buf ); // 古いupdates2.dauを削除 ::DeleteFile( (base_folder+"\\updates2.dau").c_str() ); ::DeleteFile( (base_folder+"\\ghost\\master\\updates2.dau").c_str() ); // 対象ファイル選定 cout << "対象ファイルを選定中" << endl; strvec allow_files_vec, deny_files_vec; stringset deny_files_set; split(conf["allow_files"], byte_value_2, allow_files_vec); split(conf["deny_files"], byte_value_2, deny_files_vec); // 除外ファイルが(存在すれば)set化 for (strvec::iterator i=deny_files_vec.begin() ; i!=deny_files_vec.end() ; ++i) { string full_path = base_folder+"\\"+*i; replace(full_path, "/", "\\"); string folder_name = get_folder_name(full_path); WIN32_FIND_DATA fd; HANDLE h = ::FindFirstFile(full_path.c_str(), &fd); if ( h == INVALID_HANDLE_VALUE ) continue; do { if ( fd.dwFileAttributes&FILE_ATTRIBUTE_DIRECTORY ) continue; deny_files_set.insert(folder_name+"\\"+fd.cFileName); } while ( ::FindNextFile(h, &fd) ); ::FindClose(h); } // 対象ファイルを、除外対象に一致しないことを確認しつつfiles化 for (strvec::iterator i=allow_files_vec.begin() ; i!=allow_files_vec.end() ; ++i) { string full_path = base_folder+"\\"+*i; replace(full_path, "/", "\\"); string folder_name = get_folder_name(full_path); WIN32_FIND_DATA fd; HANDLE h = ::FindFirstFile(full_path.c_str(), &fd); if ( h == INVALID_HANDLE_VALUE ) { sender << *i << "に該当するファイルがありません。" << endl; continue; } int n=0; do { if ( fd.dwFileAttributes&FILE_ATTRIBUTE_DIRECTORY ) continue; string filename=folder_name+"\\"+fd.cFileName; if ( deny_files_set.find(filename) == deny_files_set.end() ) { files.push_back(filename); ++n; } } while ( ::FindNextFile(h, &fd) ); ::FindClose(h); if ( n==0 ) { sender << *i << "に該当するファイルがありません。" << endl; continue; } } if ( files.size()==0 ) { error("対象となるファイルが存在しません。"); return 1; } else { // 本家のupdates2.dauに構造を合わせるためのリストのソート list<string> OLD = files; // 複製を作る files.clear(); list<string>& NEW = files; // 空っぽのほう // まずは単なるアルファベットソート files.sort(); // まずはshellフォルダの中身を移行 list<string>::iterator i;; for (i=OLD.begin(); i!=OLD.end() ;) { if ( compare_head(get_folder_name(*i), base_folder+"\\shell\\") ) { NEW.push_back(*i); i=OLD.erase(i); } else ++i; } // 続いてghostフォルダの中身を移行 for (i=OLD.begin(); i!=OLD.end() ;) { if ( compare_head(get_folder_name(*i), base_folder+"\\ghost\\") ) { NEW.insert(NEW.begin(), *i); // 反転しないように i=OLD.erase(i); } else ++i; } // ghost, shell,ルート以外のフォルダの中身を移行(あるのか?) for (i=OLD.begin(); i!=OLD.end() ;) { if ( !compare_head(get_folder_name(*i), base_folder) ) { NEW.push_back(*i); i=OLD.erase(i); } else ++i; } // 残り。ルートフォルダの中身を移行 NEW.insert(NEW.end(), OLD.begin(), OLD.end()); OLD.clear(); } sender << "updates2.dauを作成中" << endl; bool makeUpdates2(string base_folder, const list<string>& files); if ( !makeUpdates2(szPath, files) ) { error("updates2.dauが作成できませんでした。"); return 1; } sender << "updates2.dauを作成完了" << endl; // 同じものをコピー ::CopyFile( (base_folder+"\\updates2.dau").c_str(), (base_folder+"\\ghost\\master\\updates2.dau").c_str(), FALSE ); if ( conf["is_create_archive"]!="0") { sender << "zip/narアーカイブを作成中" << endl; if ( !is_exist_file(base_folder+"\\install.txt") ) { error("zip/narアーカイブを作成するためには、ゴーストのルートフォルダにinstall.txtが必要です。"); return false; } SYSTEMTIME st; char buf[10]; ::GetLocalTime(&st); sprintf(buf, "%02d", st.wYear%100); replace(conf["archive_filename"], "%year", buf); sprintf(buf, "%04d", st.wYear); replace(conf["archive_filename"], "%year4", buf); sprintf(buf, "%02d", st.wMonth); replace(conf["archive_filename"], "%month", buf); sprintf(buf, "%02d", st.wDay); replace(conf["archive_filename"], "%day", buf); sprintf(buf, "%02d", st.wHour); replace(conf["archive_filename"], "%hour", buf); sprintf(buf, "%02d", st.wMinute); replace(conf["archive_filename"], "%minute", buf); sprintf(buf, "%02d", st.wSecond); replace(conf["archive_filename"], "%second", buf); if ( (conf["archive_local_folder"])[ conf["archive_local_folder"].size()-1 ] != '\\' ) conf["archive_local_folder"] += "\\"; string archive_filename = conf["archive_local_folder"]+conf["archive_filename"]; // 既にあるなら削除しておく ::DeleteFile(archive_filename.c_str()); /* string cmd = "zip.exe"; string opt = "-r " + archive_filename; for (list<string>::const_iterator i=files.begin(); i!=files.end() ; ++i ) { string filename(*i); opt += string(" ") + (filename.c_str()+base_folder.size()+1); } opt += " ghost\\master\\updates2.dau"; if ( opt.find(" install.txt")==string::npos ) opt += " install.txt"; // nar時には必須なので。 opt += " > nul"; */ //system((cmd+" "+opt).c_str()); //::ShellExecute(NULL, NULL, (cmd+" "+opt).c_str(), NULL, NULL, SW_SHOW); //if ( !is_exist_file(archive_filename) ) { list<string> zip_files; bool exist_install_txt=false; for (list<string>::const_iterator i=files.begin(); i!=files.end() ; ++i ) { zip_files.push_back(i->c_str()+base_folder.size()+1); if ( zip_files.back()=="install.txt" ) exist_install_txt = true; } zip_files.push_back("ghost\\master\\updates2.dau"); if ( !exist_install_txt ) zip_files.push_back("install.txt"); if ( !makeZip(archive_filename, zip_files) ) { error("アーカイブファイル'"+archive_filename+"'が作成できませんでした。"); } else { sender << "zip/narアーカイブ '" << archive_filename << "' を作成完了" << endl; } } files.push_back(base_folder+"\\updates2.dau"); // フォルダ別に分離格納 as global // map<string, set<string> > files_on_dir; // dirname : filenames { for (list<string>::iterator i=files.begin(); i!=files.end() ; ++i) { string rel_path = (i->c_str()+base_folder.size()+1); files_on_dir[ get_folder_name(rel_path) ].insert( get_file_name(rel_path) ); } } bool uploadFiles(const string&, list<string>&); if ( conf["is_upload"]!="0") uploadFiles(szPath, files); // ルートのupdates2.dauはアップロードした後に削除。 ::DeleteFile( (base_folder+"\\updates2.dau").c_str() ); return 0; }