[
  {
    "path": ".gitignore",
    "content": "## Ignore Visual Studio temporary files, build results, and\n## files generated by popular Visual Studio add-ons.\n\n# User-specific files\n*.suo\n*.user\n*.sln.docstates\n\n# Build results\n\n[Dd]ebug/\n[Rr]elease/\nx64/\nbuild/\n[Bb]in/\n[Oo]bj/\n\n# Enable \"build/\" folder in the NuGet Packages folder since NuGet packages use it for MSBuild targets\n!packages/*/build/\n\n# MSTest test Results\n[Tt]est[Rr]esult*/\n[Bb]uild[Ll]og.*\n\n*_i.c\n*_p.c\n*.ilk\n*.meta\n*.obj\n*.pch\n*.pdb\n*.pgc\n*.pgd\n*.rsp\n*.sbr\n*.tlb\n*.tli\n*.tlh\n*.tmp\n*.tmp_proj\n*.log\n*.vspscc\n*.vssscc\n.builds\n*.pidb\n*.log\n*.scc\n*.json\n\n# Visual C++ cache files\nipch/\n*.aps\n*.ncb\n*.opensdf\n*.sdf\n*.cachefile\n\n# Visual Studio profiler\n*.psess\n*.vsp\n*.vspx\n\n# Guidance Automation Toolkit\n*.gpState\n\n# ReSharper is a .NET coding add-in\n_ReSharper*/\n*.[Rr]e[Ss]harper\n\n# TeamCity is a build add-in\n_TeamCity*\n\n# DotCover is a Code Coverage Tool\n*.dotCover\n\n# NCrunch\n*.ncrunch*\n.*crunch*.local.xml\n\n# Installshield output folder\n[Ee]xpress/\n\n# DocProject is a documentation generator add-in\nDocProject/buildhelp/\nDocProject/Help/*.HxT\nDocProject/Help/*.HxC\nDocProject/Help/*.hhc\nDocProject/Help/*.hhk\nDocProject/Help/*.hhp\nDocProject/Help/Html2\nDocProject/Help/html\n\n# Click-Once directory\npublish/\n\n# Publish Web Output\n*.Publish.xml\n*.pubxml\n\n# NuGet Packages Directory\n## TODO: If you have NuGet Package Restore enabled, uncomment the next line\n#packages/\n\n# Windows Azure Build Output\ncsx\n*.build.csdef\n\n# Windows Store app package directory\nAppPackages/\n\n# Others\nsql/\n*.Cache\nClientBin/\n[Ss]tyle[Cc]op.*\n~$*\n*~\n*.dbmdl\n*.[Pp]ublish.xml\n*.pfx\n*.publishsettings\n\n# RIA/Silverlight projects\nGenerated_Code/\n\n# Backup & report files from converting an old project file to a newer\n# Visual Studio version. Backup files are not needed, because we have git ;-)\n_UpgradeReport_Files/\nBackup*/\nUpgradeLog*.XML\nUpgradeLog*.htm\n\n# SQL Server files\nApp_Data/*.mdf\nApp_Data/*.ldf\n\n# =========================\n# Windows detritus\n# =========================\n\n# Windows image file caches\nThumbs.db\nehthumbs.db\n\n# Folder config file\nDesktop.ini\n\n# Recycle Bin used on file shares\n$RECYCLE.BIN/\n\n# Mac crap\n.DS_Store\n\n.vs/\n.vscode/\nReadMe/\npackages/\n/output\n/Sync/bin*\n"
  },
  {
    "path": ".gitmodules",
    "content": "[submodule \"OfficalPlugins\"]\n\tpath = OfficalPlugins\n\turl = https://github.com/Deliay/SyncPlugin.git\n"
  },
  {
    "path": "LICENSE",
    "content": "MIT License\n\nCopyright (c) 2017-2018 OsuSync\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
  },
  {
    "path": "Language/de-DE/BanManagerPlugin.DefaultLanguage.lang",
    "content": "[de-DE]\nLANG_HELP_BAN=Sperre Benutzer/ID/Regex ,Nachrichten werden über IRC versendet.\nLANG_HELP_UNBAN=Entsperre Benutzer/ID/Regex.\nLANG_HELP_WHITELIST=Füge Benutzer/ID/Regex zur Whitelist hinzu, um das versenden von Nachrichten über IRC zu ermöglichen.\nLANG_HELP_REMOVE_WHITELIST=Entferne Benutzer/ID/Regex aus der Whitelist.\nLANG_HELP_ACCESS=Setze Berechtigungen zum Versenden von Nachrichten über IRC.\nLANG_HELP_LIST=Liste die Whitelist oder die Sperrliste auf.\nLANG_ERR_COMMAND=Befehlsfehler.\n"
  },
  {
    "path": "Language/de-DE/BeatmapSuggest.DefaultLanguage.lang",
    "content": "[de-DE]\nLANG_GET_BEATMAP_FAILED=Erhalten von Beatmap Metadaten {0} schlug fehl,Fehlermeldung: {1}.\nLANG_SUGGEST_MEG={0} fordert dich auf, folgende Beatmap zu spielen [{1} {2}] || [{3} herunterladen] || [{4} Verweis] oder werwende \"?dl\"/\"?dl all\".\nLANG_NOT_FOUND_ERR=Es kann kein passender Inhalt gefunden werden oder die BeatmapSet ID ist ungültig.\nLANG_UNKNOWN_TITLE=<Unbekannter Titel>\nLANG_GET_BEATMAP_TIME_OUT=Zeitüberschreitung beim erhalten des Beatmap Titel {0}, Aufgabenstatus{1}.\nLANG_INVAILD_ID=Ungültige ID {0}.\nLANG_UNKOWN_PARAM=Unbekannter Parameter {0}.\nLANG_ERROR_INFO_IMCOMPLETE=Unvollständige Informationen.\nLANG_NO_API_KEY_NOFITY=Der osu!api Schlüssel-eintrag fehlt in der Konfigurationsdatei config.ini. Bitte vervollständige diesen mit Hilfe von https://osu.ppy.sh/p/api\nLANG_START_DOWNLOAD=Der Download der Beatmap {0} hat begonnen.\nLANG_FINISH_DOWNLOAD=Der Download der Beatmap {0} wurde abgeschlossen.\nLANG_FAILED_DOWNLOAD=Der Downloaded der Beatmap {0} schlug fehl, Fehlermeldung: {1}.\nLANG_DOWNLOAD_TASK_COUNT=Der Download von {0} Beatmaps hat begonnen.\n"
  },
  {
    "path": "Language/de-DE/ConfigGUI.DefaultLanguage.lang",
    "content": "[de-DE]\nBUTTON_OPEN=Öffne\nBUTTON_BROWSE=Durchsuche\nBUTTON_FONT=Schriftart\nBUTTON_COLOR=Farbe\nWINDOW_TITLE=Konfiguration\nBUTTON_SAVE=Speichere\nLABEL_SAVED=Gespeichert!\nLABEL_SAVED_SAVING=Speichere...\nWINDOW_TITLE_REQUIRE_RESTART=Bitte starten sie neu um einige Einstellungen zu übernehmen.\n"
  },
  {
    "path": "Language/de-DE/DefaultGUI.Language.lang",
    "content": "[de-DE]\nUI_DISPLAY=Öffne die UI\nUI_TIPS_BOTIRC=BotIRC\nUI_TIPS_BOTIRC_PASS=IRC Passwort\nUI_TIPS_STATUS=Status\nUI_TIPS_DANMAKU=LiveID\nUI_TIPS_OSU_IRC=osu!Chat\nUI_TIPS_TARGET_IRC=ToIRC\nUI_BOTTON_START=Start\nUI_BOTTON_STOP=Stop\nUI_BOTTON_LOGIN_DANMAKU=Source Login\nUI_BOTTON_SWITCH_CON=Wechsel zur command-line-interface UI\nUI_BOTTON_EXIT=Beende\nUI_TIPS_LIVE_ID=LiveID\nUI_TIPS_LIVE_SOURCE=Quelle\nUI_INFO_RESTART_REQ=Um die Änderung des Livestreams zu übernehmen, muss Sync neugestartet werden. Starte jetzt neu? \n"
  },
  {
    "path": "Language/de-DE/DefaultPlugin.Language.lang",
    "content": "[de-DE]\nLANG_COMMANDS_LOGIN=login Benutzername> [Passwort] um Nachrichten zur Quelle zu versenden.\nLANG_COMMANDS_EXIT=Arbeiten einstellen und beenden.\nLANG_COMMANDS_CLEAR=Bildschirm löschen.\nLANG_COMMANDS_STATUS=Status der aktuellen Arbeit abrufen.\nLANG_COMMANDS_STOP=Laufende Arbeit stoppen.\nLANG_COMMANDS_START=Synchronisierung starten.\nLANG_COMMANDS_HELP=Hilfemitteilung drucken.\nLANG_COMMANDS_SOURCEMSG=sourcemsg <message> Eine Nachricht zum Test an die Quelle senden.\nLANG_COMMANDS_CLIENTMSG=clientmsg <message> Eine Nachricht zum Test an den Kunden senden.\nLANG_COMMANDS_SOURCES=Alle Quellen-Liste erhalten oder eine Quelle setzen.\nLANG_COMMANDS_MSGMGR=--help-argument ausführen, um Hilfe zu erhalten.\nLANG_COMMANDS_FILTERS=Alle verfügbaren Filter auflisten.\nLANG_COMMANDS_DISABLE= disable <plugin name>, um OnDisable() Funtion des Plugins aufzurufen.\nLANG_COMMANDS_SWITCH_CLIENT=switchclient [client], um uu einem bestimmten Client wechseln.\nLANG_COMMANDS_SOURCELOGIN=sourcelogin [user] [passwd], um sich an der Quelle anzumelden.\nLANG_COMMANDS_RESTART=Anwendung neu starten.\nLANG_COMMANDS_LANG=lang [cultureName] Sprache holen/einstellen.\nLANG_COMMANDS_LISTLANG=listlang [--all] Liste (unterstützte/alle) Sprachen.\nLANG_COMMANDS_BILIBILI=setbili <roomID> Quelle der Bilibili RoomID setzen.\nLANG_COMMANDS_FILTERS_ITEM=Item \nLANG_COMMANDS_FILTERS_OBJ=Object \nLANG_COMMANDS_SET_OSU_BOT=setosubot (botirc) (botirc_pw) (targetirc), um das OSUIRCBot-Konten setzen.\nLANG_COMMANDS_BOTIRC_CURRENT=Aktuelles BotIRC: {0:S}.\nLANG_COMMANDS_BOTIRC_SET=Aktuelles BotIRC auf {0:S} setzen.\nLANG_COMMANDS_IRC_CURRENT=Aktuelles IRC Ziel: {0:S}.\nLANG_COMMANDS_IRC_SET=Aktuelles IRC Ziel auf {0:S} festlegen.\nLANG_COMMANDS_TARGET_CURRENT=Aktuelle LiveID: {0:S}\nLANG_COMMANDS_TARGET_SET=Aktuelle LiveID setzen auf\nLANG_COMMANDS_CLIENT_NAME=Client\nLANG_COMMANDS_CLIENT_AUTHOR=Autor\nLANG_COMMANDS_SOURCES_NAME=Quelle\nLANG_COMMANDS_SOURCES_AUTHOR=Autor\nLANG_COMMANDS_CURRENT=Auf {0:S} festlegen.\nLANG_COMMANDS_DANMAKU_NOT_SUPPORT=Die aktuelle Quelle ist keine Sendable-Quelle.\nLANG_COMMANDS_EXIT_DONE=Ausführung beendet, bitte schließen Sie das Fenster.\nLANG_COMMANDS_CHAT_IRC_NOTCONNECT= IRC nicht verbunden.Nachricht können nicht an den Client versendet werden.\nLANG_COMMANDS_DANMAKU_REQUIRE_LOGIN=Anmeldung zur Quelle erforderlich.\nLANG_COMMANDS_START_ALREADY_RUN= Eine Instanz der Anwendung läuft bereits.\nLANG_COMMANDS_ARGUMENT_WRONG=Falsches Argument.\nLANG_COMMANDS_MSGMGR_HELP=\\n--status \\n--limit <level> :Lower Level für weniger Nachrichten\\n--option <type> :auto/force_all/force_limit.\nLANG_COMMANDS_MSGMGR_LIMIT=Begrenzt...\nLANG_COMMANDS_MSGMGR_FREE=Unbegrenzt\nLANG_COMMANDS_MSGMGR_STATUS=MessageManager Modus:{4:S},status:{0:D},queueCount/limitCount/recoverTime:{1}/{2}/{3}.\nLANG_COMMANDS_MSGMGR_LIMIT_SPEED_SET=Geschwindigkeitsbegrenzung auf {0} setzen.\nLANG_COMMANDS_MSGMGR_LIMIT_STYPE_SET=Grenzwerttyp auf {0} setzen.\nLANG_SEND_COOKIE_SAVED=Anmelde-Informationen erfolgreich gespeichert.\nLANG_SEND_DONE=Nachricht gesendet.\nLANG_DOUYU_FAIL=Überprüfung des Verbindungsstatus fehlgeschlagen! Außer {0}, Ergebnis {1}\nLANG_DOUYU_AUTH_SUCC=Authentifizierung zu Douyutv erfolgreich.\nLANG_DOUYU_DANMAKU=Erhaltene Nachricht: {0}:{1}\nLANG_DOUYU_GIFT=Geschenk\nLANG_BILIBILI_ONLINECHANGE=Online änderung: {0}\nLANG_COMMANDS_CLIENTUSERMSG=chatuser <Benutzername> <Nachricht>  IRC-Nachricht an Live mit angegebenem Benutzernamen senden\nLANG_OSUIRC_LOGIN_SUCCESS=Anmeldung erfolgreich.\nLANG_OSUIRC_LOGIN_FAILED=Versuch, die Verbindung zu osu!irc wiederherzustellen, fehlgeschlagen, Grund:{0}\nLANG_OSUIRC_NETWORK_INTERRUPTED= Das Netzwerk ist unterbrochen, versuchen Sie nun, die Verbindung wieder herzustellen.\nLANG_OSUIRC_RECIVER_FINISHED=Der Empfänger-Thread ist beendet.\n"
  },
  {
    "path": "Language/de-DE/NowPlaying.Languages.lang",
    "content": "[de-DE]\nOSU_PATH_NOT_SET=osu! Pfad ist leer. Die Anwendung sucht nach einer laufenden osu! Instanz.\nFIND_OSU_PATH=Kann den osu! Pfad nicht finden: {0:S}\nOSU_PATH_FAIL=Kann den osu! Pfad nicht finden. Weitere Informationen versteckt.\nERROR_WHILE_FIND_PATH=Fehler bei der Suche nach dem osu! Pfad {0:S}\nUNKNOWN_COMMAND=Unbekannter Befehl {0:S}\nSTATUS_PLAYING=Spiele\nSTATUS_EDITING=Bearbeite\nSTATUS_OTHER=Höre\nSTATUS_TIP_INFO=Ich bin {0:S}{1:S}\nSTATUS_TIP_INFO_WRAP=Ich bin {0:S}{1:S}\nERROR_WHILE_SEARCH_MAP=Fehler bei der Suche nach {0:S}-{1:S} [{2:S}] {3:S}\nCONSOLE_OUTPUT_RESULT=Datei:{0:S} ({1}ms) HP/CS/AR/OD: {2}{3}{4}{5}\nOUTPUT_RESULT=Aktuell {0}:{1}\nCURRENT_IDLE=Aktuell im Lehrlauf\n"
  },
  {
    "path": "Language/de-DE/OsuRTDataProvider.DefaultLanguage.lang",
    "content": "[de-DE]\nLANG_OSU_NOT_FOUND=[OsuRTDataProvider][ID:{0}]Kann den osu!.exe Prozess nicht finden.\nLANG_OSU_FOUND=[OsuRTDataProvider][ID:{0}]osu!.exe Prozess gefunden.\nLANG_INIT_PLAY_FINDER_FAILED=[OsuRTDataProvider][ID:{0}]Initialisierung von PlayFinder schlug fehl! Wiederhole in {1} Sekunden.\nLANG_INIT_PLAY_FINDER_SUCCESS=[OsuRTDataProvider][ID:{0}]Initialisierung von PlayFinder Success!\nLANG_INIT_STATUS_FINDER_FAILED=[OsuRTDataProvider][ID:{0}]Initialisierung von StatusFinder schlug fehl! Wiederhole in {1} Sekunden.\nLANG_INIT_STATUS_FINDER_SUCCESS=[OsuRTDataProvider][ID:{0}]Initialisierung von StatusFinder erfolgreich!\nLANG_INIT_BEATMAP_FINDER_FAILED=[OsuRTDataProvider][ID:{0}]Initialisierung von BeatmapFinder schlug fehl! Wiederhole in {1} Sekunden.\nLANG_INIT_BEATMAP_FINDER_SUCCESS=[OsuRTDataProvider][ID:{0}]Initialisierung von BeatmapFinder erfolgreich!\nLANG_INIT_MODE_FINDER_FAILED=[OsuRTDataProvider][ID:{0}]Initialisierung von ModeFinder Failed! schlug fehl! Wiederhole in {1} Sekunden.\nLANG_INIT_MODE_FINDER_SUCCESS=[OsuRTDataProvider][ID:{0}]Initialisierung von ModeFinder erfolgreich!\nLANG_BEATMAP_NOT_FOUND=Beatmap nicht gefunden.\n"
  },
  {
    "path": "Language/de-DE/RealTimePPDisplayer.DefaultLanguage.lang",
    "content": "[de-DE]\nUI_MENU_TOPMOST=Oberste\nTEXT_MODE_OUTPUT_PATH_FORMAT=[RealTimePPDisplayer]PP Datei: {0}\nMMF_MODE_OUTPUT_PATH_FORMAT=[RealTimePPDisplayer]Speicherzuordnungsdatei: {0}\n"
  },
  {
    "path": "Language/de-DE/RecentlyUserQuery.DefaultLanguage.lang",
    "content": "[de-DE]\nLANG_HELP=\\nDies ist eine Hilfe für IRC Befehle. Hänge dazu ein \"?\" am Anfang der Nachrichten ein.\\nrecently --status | Rufe den Status des aktuellen Protokolls ab (osu! IRC nicht verfügbar)\\n recently --u <Benutzername> | Rufe <Benutzername> Protokoll ab (Nicht empfohlen für osu! IRC Verwendung)\\n recently --i <BenutzerID> | Rufe <Benutzername> Protokoll ab  (Nicht empfohlen für osu! IRC Verwendung)\\n recently | Rufe den aktuellen Benutzernamen und die ID ab. Die ID kann verwendet für Aktionen wie \"?ban --i\" (osu! IRC unterstützt) verwendet werden\\nrecently --disable | Nachrichtenprotokollierung deaktiviert. Alle Protokolle werden gelöscht (osu! IRC unterstützt)\\recently --start | Protokollierung erneut starten (osu! IRC unterstützt)\\recently  --realloc <Kapazität> | Kapazität der Protokolle neu zuweisen, um mehr Protokolle zu speichern (osu! IRC unterstützt)\\recently --recently | Aktuelle Benutzer-ID ermitteln.\nLANG_MSG_STATUS=MessageRecord Status: {0} | recordCount/Kapazität: {1}/{2}\nLANG_RUNNING=Läuft\nLANG_STOP=Angehalten\nLANG_MSG_DISABLE=Nachrichtenprotokollierung deaktiviert, alle Protokolle wurden gelöscht.\nLANG_MSG_START=Die Nachrichtenprotokollierung wurde gestartet.\nLANG_MSG_REALLOC_ERR=MessageRecord: Falsche Anweisungen.\nLANG_MSG_REALLOC=Sie können jetzt den Nachrichtenverlauf für {0} protokollieren.\nLANG_MSG_NOTIMPLENT=Nicht implementiert.\nLANG_MSG_UNKNOWNCOMMAND=Unbekannter Befehl.\n"
  },
  {
    "path": "Language/de-DE/Sync.Tools.DefaultI18n.lang",
    "content": "[de-DE]\nLANG_Loading=Lade....\nLANG_Plugins={0:D} Plugins geladen.\nLANG_Sources={0:D} Quellen geladen.\nLANG_Client={0:D} Clients geladen.\nLANG_Error=KÖNNEN WARPPER NICHT INITIALISIEREN! ÜBERPRÜFEN SIE IHRE PLUGINS.\nLANG_Commands={0:D} Befehle geladen.\nLANG_Filters={0:D} Filter geladen.\nLANG_Ready=Fertig.\nLANG_RqueireLogin=Bitte melden Sie sich an.\nLANG_AccountName=Benutzername:\nLANG_AccountPw=Passwort:\nLANG_AccountSave=Account erfolgreich gespeichert!\nLANG_Start=Starte...\nLANG_Stopping=Stoppe...\nLANG_Restarting=Starte neu...\nLANG_LoadingPlugin=Lade {0:S} ...\nLANG_LoadPluginErr=Kann {0:S} nicht laden ({1:S})\nLANG_NotPluginErr={0:S} ist kein Standartplugin ({1:S})\nLANG_NotConfig=Bitte nehmen sie Grundeinstellungen an der Konfiguration 'config.ini' vor.\nLANG_NoSource=Kann keine Quelle für Warpper finden. Bitte installieren Sie eine Quelle.\nLANG_MissSource=Ich kann die Quelle in den spezifischen Einstellungen nicht finden.\nLANG_SetSource={0:S} als aktuelle Live-Quelle festlegen \nLANG_SupportSend=Nach der Anmeldung können Sie eine Nachricht über die Quelle senden (Befehl: login [Benutzer] [Passwort])\nLANG_CertLength=Länge des Zertifikats: {0:D}\nLANG_CertExist=Aktuelles Zertifikat vorhanden, überschreiben Sie es mit dem Anmelde-Befehl.\nLANG_SendNotReady=Die aktuelle Quelle ist nicht bereit, an Danmaku zu senden.\nLANG_UnknowCommand=Unbekannter Befehl, geben Sie 'help' für die Befehlsliste ein.\nLANG_CommandFail=Befehlsausführung fehlgeschlagen, geben Sie 'help' für die Befehlsliste ein.\nLANG_ConfigFile=Einstellungen\nLANG_UserCount=Änderung der Benutzeranzahl : {0:D}\nLANG_UserCount_Change=Zuschauer {0:S} to {1:D}\nLANG_UserCount_Change_Increase=erhöhen\nLANG_UserCount_Change_Decrease=reduzieren\nLANG_Source_Disconnecting=Trennen der Quellverbindung...\nLANG_Source_Disconnected=Die Verbindung zum Quellserver wurde getrennt. Wiederholung nach 3 Sekunden.\nLANG_Source_Disconnected_Succ=Erfolgreiche Trennung der Verbindung zum Quellserver.\nLANG_Source_Connect=Verbindung zum Quellserver herstellen...\nLANG_Source_Connected_Succ=Erfolg mit dem Quellenserver verbunden!\nLANG_Current_Online=Aktuell online: {0:D}\nLANG_Gift_Sent=Ich sende {O:D} (1 S) zu dir!\nLANG_Config=Einstellungen: \nLANG_Config_Status_OK=OK, LiveID:{0}\nLANG_Config_Status_Fail=Konfiguration fehlgeschlagen.\nLANG_Source=Quelle {0:S}: \nLANG_IRC=Client:\nLANG_Danmaku=Danmaku sendet:\nLANG_Status_Connected=Verbunden\nLANG_Status_NotConenct=Leerlauf\nLANG_Loading_Config=Laden von Einstellungen...\\n\nLANG_Welcome=osu!Live Sync Version {0:S} \nLANG_Help=Geben Sie den Befehl 'help' für Hilfe ein\nLANG_Command=Befehl\nLANG_Command_Description=Beschreibung\nLANG_Plugin_Cycle_Reference=Das Plugin wird zyklisch referenziert. {0:S} wird ohne den abhängigen Baum nicht geladen.\nLANG_IRC_Connecting=[Client] verbindend...\nLANG_IRC_Disconnect=Trennen der Verbindung zum IRC-Server...\nLANG_IRC_Connect_Timeout=Client-Verbindung fehlgeschlagen, bitte überprüfen Sie Ihre Internetverbindung.\nLANG_IRC_Ready=[Client] ist bereit!\nLANG_MsgMgr_Limit=Der Nachrichtenbegrenzer ist eingeschaltet, das Danmaku nur mit dem Präfix ?send wird an irc gesendet.\nLANG_MsgMgr_Free=Der Nachrichtenbegrenzer ist ausgeschaltet.\nLANG_Instance_Exist=Die Anwendung ist bereits gestarter.\nLANG_COMMANDS_LOGIN=login Benutzername> [Passwort] um Nachrichten zur Quelle zu versenden.\nLANG_COMMANDS_LOGIN=login <user> [pass] login to source to send message to source\nLANG_COMMANDS_EXIT=Arbeiten einstellen und beenden.\nLANG_COMMANDS_EXIT=Stop sync and Exit\nLANG_COMMANDS_CLEAR=Bildschirm löschen.\nLANG_COMMANDS_CLEAR=Clear output screen\nLANG_COMMANDS_STATUS=Status der aktuellen Arbeit abrufen.\nLANG_COMMANDS_STATUS=Get status of current Source and Client\nLANG_COMMANDS_STOP=Laufende Arbeit stoppen.\nLANG_COMMANDS_STOP=Stop sync\nLANG_COMMANDS_START=Synchronisierung starten.\nLANG_COMMANDS_START=Start sync\nLANG_COMMANDS_HELP=Hilfemitteilung drucken.\nLANG_COMMANDS_HELP=Print help message\nLANG_COMMANDS_SOURCEMSG=sourcemsg <message> Eine Nachricht zum Test an die Quelle senden.\nLANG_COMMANDS_SOURCEMSG=sourcemsg <message> Send a message to source for test\nLANG_COMMANDS_CLIENTMSG=clientmsg <message> Eine Nachricht zum Test an den Kunden senden.\nLANG_COMMANDS_CLIENTMSG=clientmsg <message> Send a message to client for test\nLANG_COMMANDS_SOURCES=Alle Quellen-Liste erhalten oder eine Quelle setzen.\nLANG_COMMANDS_SOURCES=get all source list or set a source\nLANG_COMMANDS_MSGMGR=--help-argument ausführen, um Hilfe zu erhalten.\nLANG_COMMANDS_MSGMGR=Execute with '--help' to get help\nLANG_COMMANDS_FILTERS=Alle verfügbaren Filter auflisten.\nLANG_COMMANDS_FILTERS=List all available filters.\nLANG_COMMANDS_DISABLE= disable <plugin name>, um OnDisable() Funtion des Plugins aufzurufen.\nLANG_COMMANDS_DISABLE=disable <plugin name> Invoke Plugin OnDisable() function\nLANG_COMMANDS_SWITCH_CLIENT=switchclient [client], um uu einem bestimmten Client wechseln.\nLANG_COMMANDS_SWITCH_CLIENT=switchclient [client] Switch to specific client\nLANG_COMMANDS_SOURCELOGIN=sourcelogin [user] [passwd], um sich an der Quelle anzumelden.\nLANG_COMMANDS_SOURCELOGIN=sourcelogin [user] [passwd] Login to source\nLANG_COMMANDS_RESTART=Anwendung neu starten.\nLANG_COMMANDS_RESTART=Restart Sync\nLANG_COMMANDS_LANG=lang [cultureName] Sprache holen/einstellen.\nLANG_COMMANDS_LANG=lang [cultureName] Get/Set language\nLANG_COMMANDS_LISTLANG=listlang [--all] Liste (unterstützte/alle) Sprachen.\nLANG_COMMANDS_FILTERS_ITEM=Item\nLANG_COMMANDS_FILTERS_ITEM=Item\nLANG_COMMANDS_FILTERS_OBJ=Object \nLANG_COMMANDS_CLIENT_NAME=Client\nLANG_COMMANDS_CLIENT_NAME=Client\nLANG_COMMANDS_CLIENT_AUTHOR=Autor\nLANG_COMMANDS_CLIENT_AUTHOR=Author\nLANG_COMMANDS_SOURCES_NAME=Quelle\nLANG_COMMANDS_SOURCES_NAME=Source\nLANG_COMMANDS_SOURCES_AUTHOR=Autor\nLANG_COMMANDS_SOURCES_AUTHOR=Author\nLANG_COMMANDS_CURRENT=Auf {0:S} festlegen.\nLANG_COMMANDS_CURRENT=Set to {0:S}\nLANG_COMMANDS_DANMAKU_NOT_SUPPORT=Die aktuelle Quelle ist keine Sendable-Quelle.\nLANG_COMMANDS_CHAT_IRC_NOTCONNECT= IRC nicht verbunden.Nachricht können nicht an den Client versendet werden.\nLANG_COMMANDS_CHAT_IRC_NOTCONNECT=osu!irc not connect properly, can't send message.\nLANG_COMMANDS_DANMAKU_REQUIRE_LOGIN=Anmeldung zur Quelle erforderlich.\nLANG_COMMANDS_DANMAKU_REQUIRE_LOGIN=Require Login to source!\nLANG_COMMANDS_START_ALREADY_RUN= Eine Instanz der Anwendung läuft bereits.\nLANG_COMMANDS_START_ALREADY_RUN=An instance is already running\nLANG_COMMANDS_ARGUMENT_WRONG=Falsches Argument.\nLANG_COMMANDS_ARGUMENT_WRONG=Wrong argument.\nLANG_COMMANDS_MSGMGR_HELP=\\n--status \\n--limit <level> :Lower Level für weniger Nachrichten\\n--option <type> :auto/force_all/force_limit.\nLANG_COMMANDS_MSGMGR_HELP=\\n--status \\n--limit <level> :Lower Level for less messaging\\n--option <type> :Auto/ForceAll/ForceLimit/DisableAll\nLANG_COMMANDS_MSGMGR_LIMIT=Begrenzt...\nLANG_COMMANDS_MSGMGR_LIMIT=MessageManager is limited...\nLANG_COMMANDS_MSGMGR_FREE=Unbegrenzt\nLANG_COMMANDS_MSGMGR_FREE=MessageManager is freed...\nLANG_COMMANDS_MSGMGR_STATUS=MessageManager Modus:{4:S},status:{0:D},queueCount/limitCount/recoverTime:{1}/{2}/{3}.\nLANG_COMMANDS_MSGMGR_STATUS=MessageManager mode:{4:S},status:{0:D},queueCount/limitCount/recoverTime:{1}/{2}/{3}\nLANG_COMMANDS_MSGMGR_LIMIT_SPEED_SET=Geschwindigkeitsbegrenzung auf {0} setzen.\nLANG_COMMANDS_MSGMGR_LIMIT_SPEED_SET=Set speed limit to {0}\nLANG_COMMANDS_MSGMGR_LIMIT_STYPE_SET=Grenzwerttyp auf {0} setzen.\nLANG_COMMANDS_START_NO_SOURCE=Es muss vor dem Start eine Quelle ausgewählt.\nLANG_COMMANDS_START_NO_CLIENT=Es müssen vor dem Start ein Client ausgewählt sein.\nLANG_COMMANDS_CURRENT_LANG=Aktuelle Sprache: {0:S}\t{1:S}\nLANG_COMMANDS_LANG_SWITCHED=sucSprache erfolgreich umstellen auf {1:S}({0:S})\nLANG_COMMANDS_LANG_NOT_FOUND=Sprachumschaltung fehlgeschlagen! Benutze 'listlang', um alle verfügbaren Sprachen anzuzuzeigen.\nLANG_UPDATE_DONE=Update durchgeführt. Neustart, um die Aktualisierung abzuschließen.\nLANG_INSTALL_DONE=Installation abgeschlossen. Neustart, um die Installation abzuschließen.\nLANG_PLUGIN_NOT_FOUND=Plugin {0} ist nicht vorhanden.\nLANG_REMOVE_DONE=Entfernt. Neu starten, um Effekt anzuwenden.\nLANG_VERSION_LATEST_OR_CANEL={0} ist aktuell oder wurde vom Benutzer abgebrochen.\nLANG_UPDATE_CHECK_ERROR=Aktualisierung kann nicht geprüft werden für [{0}] : {1} : {2}\nLANG_UPDATE_ERROR=Update nicht möglich:  {0} : {1}\nLANG_SOURCE_NOT_SUPPORT_SEND=Die Quelle {0} unterstützt das Senden von Nachrichten noch nicht.\nLANG_NO_PLUGIN_SELECT=Es muss einen Plugin-Namen angeben werden. \nLANG_PLUGIN_DISABLED=Deaktiviert\nLANG_COMMANDS_CLIENTUSERMSG=chatuser <Benutzername> <Nachricht> Testnachricht als <Benutzername> zum IRC senden.\nLANG_COMMANDS_EXIT_DONE=Aktionsausführung beenden beendet, bitte schließen Sie das Fenster.\nLANG_NO_ANY_SOURCE=Es ist keine Quelle verfügbar, bitte überprüfen Sie den Plugin-Ordner oder geben Sie 'plugins install DefaultPlugin' ein, um das Plugin als Standardquellen zu installieren\n"
  },
  {
    "path": "Language/en-US/BanManagerPlugin.DefaultLanguage.lang",
    "content": "[en-US]\r\nLANG_HELP_BAN=ban user/id/regex ,message will be sent to irc\r\nLANG_HELP_UNBAN=unban user/id/regex\r\nLANG_HELP_WHITELIST=add user/id/regex to whitelist who can send message to irc\r\nLANG_HELP_REMOVE_WHITELIST=remove user/id/regex from whitelist\r\nLANG_HELP_ACCESS=set access level for sending messages to irc\r\nLANG_HELP_LIST=list whiltelist or banlist\r\nLANG_ERR_COMMAND=command error\r\n"
  },
  {
    "path": "Language/en-US/BeatmapSuggest.DefaultLanguage.lang",
    "content": "[en-US]\nLANG_GET_BEATMAP_FAILED=Get beatmap info {0} failed,message:{1}\nLANG_SUGGEST_MEG={0} want you to play the beatmap [{1} {2}] || [{3} dl] || [{4} mirror] or type\"?dl\"/\"?dl all\"\nLANG_NOT_FOUND_ERR=ҲƥݻidЧbeatmapSetId Can't match infomation or param ID is invaild beatmapSetId\nLANG_UNKNOWN_TITLE=<unk title>\nLANG_GET_BEATMAP_TIME_OUT=Get beatmap info {0} time out,TaskStatus{1}\nLANG_INVAILD_ID=invaild id value {0}\nLANG_UNKOWN_PARAM=unknown param {0}\nLANG_ERROR_INFO_IMCOMPLETE=Incompletable infomation\nLANG_NO_API_KEY_NOFITY=there is no ApiKey in config.ini,please provide your osu!api_key for using.ApiKey address:https://osu.ppy.sh/p/api\nLANG_START_DOWNLOAD=Start to download beatmap:{0}\nLANG_FINISH_DOWNLOAD=Beatmap downloaded {0}\nLANG_FAILED_DOWNLOAD=Downloaded {0} failed,message {1}\nLANG_DOWNLOAD_TASK_COUNT=Start to download {0} beatmaps.\n"
  },
  {
    "path": "Language/en-US/ConfigGUI.DefaultLanguage.lang",
    "content": "[en-us]\nBUTTON_OPEN=Open\nBUTTON_BROWSE=Browse\nBUTTON_FONT=Font\nBUTTON_COLOR=Color\nWINDOW_TITLE=Config\nBUTTON_SAVE=Save\nLABEL_SAVED=Saved!\nLABEL_SAVED_SAVING=Saving...\nWINDOW_TITLE_REQUIRE_RESTART=Some settings restart to take effect\n"
  },
  {
    "path": "Language/en-US/DefaultGUI.Language.lang",
    "content": "[en-US]\r\nUI_DISPLAY=Show UI\r\nUI_TIPS_BOTIRC=BotIRC\r\nUI_TIPS_BOTIRC_PASS=IRC PW\r\nUI_TIPS_STATUS=Status\r\nUI_TIPS_DANMAKU=LiveID\r\nUI_TIPS_OSU_IRC=osu!Chat\r\nUI_TIPS_TARGET_IRC=ToIRC\r\nUI_BOTTON_START=Start\r\nUI_BOTTON_STOP=Stop\r\nUI_BOTTON_LOGIN_DANMAKU=Source Login\r\nUI_BOTTON_SWITCH_CON=Switch to CMD UI\r\nUI_BOTTON_EXIT=Exit\r\nUI_TIPS_LIVE_ID=LiveID\r\nUI_TIPS_LIVE_SOURCE=Source\r\nUI_INFO_RESTART_REQ=Changing live source will be applied after restarting Sync. Restart now? \r\n"
  },
  {
    "path": "Language/en-US/DefaultPlugin.Language.lang",
    "content": "[en-US]\r\nLANG_COMMANDS_LOGIN=login <user> [pass] login to source to send message to source\r\nLANG_COMMANDS_EXIT=Stop works and Exit\r\nLANG_COMMANDS_CLEAR=Clear screen\r\nLANG_COMMANDS_STATUS=Get status of current work\r\nLANG_COMMANDS_STOP=Stop current work\r\nLANG_COMMANDS_START=Start sync\r\nLANG_COMMANDS_HELP=Print help message\r\nLANG_COMMANDS_SOURCEMSG=sourcemsg <message> Send a message to source for test\r\nLANG_COMMANDS_CLIENTMSG=clientmsg <message> Send a message to client for test\r\nLANG_COMMANDS_SOURCES=get all source list or set a source\r\nLANG_COMMANDS_MSGMGR=Execute with --help argument to get help\r\nLANG_COMMANDS_FILTERS=List all available filters.\r\nLANG_COMMANDS_DISABLE=disable <plugin name> Invoke Plugin OnDisable() function\r\nLANG_COMMANDS_SWITCH_CLIENT=switchclient [client] Switch to specific client\r\nLANG_COMMANDS_SOURCELOGIN=sourcelogin [user] [passwd] Login to source\r\nLANG_COMMANDS_RESTART=Restart application\r\nLANG_COMMANDS_LANG=lang [cultureName] Get/Set language\r\nLANG_COMMANDS_LISTLANG=listlang [--all] List (supported/all) languages\r\nLANG_COMMANDS_BILIBILI=setbili <roomID> Set Source Bilibili RoomID\r\nLANG_COMMANDS_FILTERS_ITEM=Item \r\nLANG_COMMANDS_FILTERS_OBJ=Object \r\nLANG_COMMANDS_SET_OSU_BOT=setosubot (botirc) (botirc_pw) (targetirc) Set OSUIRCBot Accounts\r\nLANG_COMMANDS_BOTIRC_CURRENT=Current BotIRC: {0:S}\r\nLANG_COMMANDS_BOTIRC_SET=Set Current BotIRC to {0:S}\r\nLANG_COMMANDS_IRC_CURRENT=Current Target IRC: {0:S}\r\nLANG_COMMANDS_IRC_SET=Set Current Target IRC to {0:S} \r\nLANG_COMMANDS_TARGET_CURRENT=Current LiveID: {0:S}\r\nLANG_COMMANDS_TARGET_SET=Set Current LiveID to \r\nLANG_COMMANDS_CLIENT_NAME=Client\r\nLANG_COMMANDS_CLIENT_AUTHOR=Author\r\nLANG_COMMANDS_SOURCES_NAME=Source\r\nLANG_COMMANDS_SOURCES_AUTHOR=Author\r\nLANG_COMMANDS_CURRENT=Set to {0:S}\r\nLANG_COMMANDS_DANMAKU_NOT_SUPPORT=Current source is not a Sendable source.\r\nLANG_COMMANDS_EXIT_DONE=Exit action execution done, please close the window.\r\nLANG_COMMANDS_CHAT_IRC_NOTCONNECT=osu! irc is not connected, cannot send message to client.\r\nLANG_COMMANDS_DANMAKU_REQUIRE_LOGIN=Require Login to source!\r\nLANG_COMMANDS_START_ALREADY_RUN=An instance is already running\r\nLANG_COMMANDS_ARGUMENT_WRONG=Wrong argument.\r\nLANG_COMMANDS_MSGMGR_HELP=\\n--status \\n--limit <level> :Lower Level for less messaging\\n--option <type> :auto/force_all/force_limit\r\nLANG_COMMANDS_MSGMGR_LIMIT=Limited...\r\nLANG_COMMANDS_MSGMGR_FREE=Unlimited\r\nLANG_COMMANDS_MSGMGR_STATUS=MessageManager mode:{4:S},status:{0:D},queueCount/limitCount/recoverTime:{1}/{2}/{3}\r\nLANG_COMMANDS_MSGMGR_LIMIT_SPEED_SET=Set speed limit to {0}\r\nLANG_COMMANDS_MSGMGR_LIMIT_STYPE_SET=Set limit type to {0}\r\nLANG_SEND_COOKIE_SAVED=Login infomation succesfully saved\r\nLANG_SEND_DONE=Message Sent.\r\nLANG_DOUYU_FAIL=Checking connection status failed! Except {0}, Result {1}\r\nLANG_DOUYU_AUTH_SUCC=Auth to Douyutv success\r\nLANG_DOUYU_DANMAKU=Recive message: {0}:{1}\r\nLANG_DOUYU_GIFT=Gift\r\nLANG_BILIBILI_ONLINECHANGE=Online change: {0}\r\nLANG_COMMANDS_CLIENTUSERMSG=chatuser <username> <message> Send irc message to live with specified username\r\nLANG_OSUIRC_LOGIN_SUCCESS=Login succesfully.\r\nLANG_OSUIRC_LOGIN_FAILED=Try to reconnect to osu!irc failed,reason:{0}\r\nLANG_OSUIRC_NETWORK_INTERRUPTED=Network is interrupted,now try to reconnect.\r\nLANG_OSUIRC_RECIVER_FINISHED=Reciver thread is finished.\r\n"
  },
  {
    "path": "Language/en-US/NowPlaying.Languages.lang",
    "content": "[en-US]\r\nOSU_PATH_NOT_SET=osu! path is empty, program will search the running osu! instance.\r\nFIND_OSU_PATH=Can't find osu! path! {0:S}\r\nOSU_PATH_FAIL=Can't find osu! path, Detailed info is disabled.\r\nERROR_WHILE_FIND_PATH=Error while searching osu! path {0:S}\r\nUNKNOWN_COMMAND=Unknown command {0:S}\r\nSTATUS_PLAYING=Playing\r\nSTATUS_EDITING=Editing\r\nSTATUS_OTHER=Listening\r\nSTATUS_TIP_INFO=I'm {0:S}{1:S}\r\nSTATUS_TIP_INFO_WRAP=I'm {0:S}{1:S}\r\nERROR_WHILE_SEARCH_MAP=Error while searching {0:S}-{1:S} [{2:S}] {3:S}\r\nCONSOLE_OUTPUT_RESULT=File:{0:S} ({1}ms) HP/CS/AR/OD: {2}{3}{4}{5}\r\nOUTPUT_RESULT=Current {0}:{1}\r\nCURRENT_IDLE=Currently idle\r\n"
  },
  {
    "path": "Language/en-US/OsuRTDataProvider.DefaultLanguage.lang",
    "content": "[en-US]\r\nLANG_OSU_NOT_FOUND=[OsuRTDataProvider][ID:{0}]Cannot find osu!.exe process\r\nLANG_OSU_FOUND=[OsuRTDataProvider][ID:{0}]Found osu!.exe process\r\nLANG_INIT_PLAY_FINDER_FAILED=[OsuRTDataProvider][ID:{0}]Init PlayFinder Failed! Retry after {1} seconds\r\nLANG_INIT_PLAY_FINDER_SUCCESS=[OsuRTDataProvider][ID:{0}]Init PlayFinder Success!\r\nLANG_INIT_STATUS_FINDER_FAILED=[OsuRTDataProvider][ID:{0}]Init StatusFinder Failed! Retry after {1} seconds\r\nLANG_INIT_STATUS_FINDER_SUCCESS=[OsuRTDataProvider][ID:{0}]Init StatusFinder Success!\r\nLANG_INIT_BEATMAP_FINDER_FAILED=[OsuRTDataProvider][ID:{0}]Init BeatmapFinder Failed! Retry after {1} seconds\r\nLANG_INIT_BEATMAP_FINDER_SUCCESS=[OsuRTDataProvider][ID:{0}]Init BeatmapFinder Success!\r\nLANG_INIT_MODE_FINDER_FAILED=[OsuRTDataProvider][ID:{0}]Init ModeFinder Failed! Retry after {1} seconds\r\nLANG_INIT_MODE_FINDER_SUCCESS=[OsuRTDataProvider][ID:{0}]Init ModeFinder Success!\r\nLANG_BEATMAP_NOT_FOUND=Beatmap not found"
  },
  {
    "path": "Language/en-US/RealTimePPDisplayer.DefaultLanguage.lang",
    "content": "[en-US]\r\nUI_MENU_TOPMOST=Topmost\r\nTEXT_MODE_OUTPUT_PATH_FORMAT=[RealTimePPDisplayer]PP File: {0}\r\nMMF_MODE_OUTPUT_PATH_FORMAT=[RealTimePPDisplayer]Memory Mapping File: {0}\r\n"
  },
  {
    "path": "Language/en-US/RecentlyUserQuery.DefaultLanguage.lang",
    "content": "[en-US]\r\nLANG_HELP=\\nThis is a help for osu!irc common commands(in osu!irc please add \"?\" at the beginning of commands)\\nrecently --status | Get the status of the current log (osu!irc unavailable)\\nrecently --u <userName> | Get the <userName> history (Not recommended for osu!irc use)\\nrecently --i <userId> | Get the <userId> history (Not recommended for osu!irc use)\\nrecently | Get recent username and ID, ID can be used to perform actions like \"?ban --i\" (osu!irc supported)\\nrecently --disable | Message logging disabled, all logs have been cleared (osu!irc supported)\\nrecently --start | Start logging again (osu!irc supported)\\nrecently --realloc <count> | Reallocate the capacity of the logs to store more logs (osu!irc supported)\\nrecently --recently | Get recent user id\\n\r\nLANG_MSG_STATUS=MessageRecord status: {0} | recordCount/Capacity : {1}/{2}\r\nLANG_RUNNING=running\r\nLANG_STOP=stopped\r\nLANG_MSG_DISABLE=Message logging disabled, all logs have been cleared\r\nLANG_MSG_START=Message logging has been started\r\nLANG_MSG_REALLOC_ERR=MessageRecord: Wrong instructions\r\nLANG_MSG_REALLOC=You can now log the message history for {0}\r\nLANG_MSG_NOTIMPLENT=Not implemented\r\nLANG_MSG_UNKNOWNCOMMAND=Unknown command."
  },
  {
    "path": "Language/en-US/Sync.Tools.DefaultI18n.lang",
    "content": "[en-US]\r\nLANG_Loading=Loading....\r\nLANG_Plugins=Loaded {0:D} Plugins\r\nLANG_Sources=Loaded {0:D} Sources\r\nLANG_Client=Loaded {0:D} Clients\r\nLANG_Error=CAN NOT INITIAL WARPPERS! CHECK YOUR PLUGINS.\r\nLANG_Commands=Loaded {0:D} Commands\r\nLANG_Filters=Loaded {0:D} Filters\r\nLANG_Ready=Done\r\nLANG_RqueireLogin=Please login to your account\r\nLANG_AccountName=Username:\r\nLANG_AccountPw=Password:\r\nLANG_AccountSave=Account succesfully saved!\r\nLANG_Start=Starting...\r\nLANG_Stopping=Stopping...\r\nLANG_Restarting=Restarting...\r\nLANG_LoadingPlugin=Load {0:S} ...\r\nLANG_LoadPluginErr=Can't load {0:S} ({1:S})\r\nLANG_NotPluginErr={0:S} not a standard plugin ({1:S})\r\nLANG_NotConfig=Please edit 'config.ini' for initial settings.\r\nLANG_NoSource=Can't find a source for warpper. Please install a source.\r\nLANG_MissSource=Can't find source from the specific settings.\r\nLANG_SetSource=Set {0:S} as current live source \r\nLANG_SupportSend=After login you can send a message via source (Command: login [user] [passwd])\r\nLANG_CertLength=Certification Length: {0:D}\r\nLANG_CertExist=Current certification exist, override it with login command\r\nLANG_SendNotReady=Current source is not ready for send a danmaku.\r\nLANG_UnknowCommand=Unknown command, type 'help' for command list.\r\nLANG_CommandFail=Command execution failed, type 'help' for command list.\r\nLANG_ConfigFile=Settings\r\nLANG_UserCount=User Count change : {0:D}\r\nLANG_UserCount_Change=Spectators {0:S} to {1:D}\r\nLANG_UserCount_Change_Increase=increase\r\nLANG_UserCount_Change_Decrease=reduce\r\nLANG_Source_Disconnecting=Disconnecting the source connection...\r\nLANG_Source_Disconnected=Source server has been disconnected. Retry after 3 sec.\r\nLANG_Source_Disconnected_Succ=Successfully disconnected from the source server.\r\nLANG_Source_Connect=Connecting to source server...\r\nLANG_Source_Connected_Succ=Success connected to source server!\r\nLANG_Current_Online=Current online : {0:D}\r\nLANG_Gift_Sent=I send {O:D} {1:S} to you!\r\nLANG_Config=Settings: \r\nLANG_Config_Status_OK=OK, LiveID:{0}\r\nLANG_Config_Status_Fail=Configuration failed.\r\nLANG_Source=Source{0:S}: \r\nLANG_IRC=Client:\r\nLANG_Danmaku=Danmaku Send:\r\nLANG_Status_Connected=Connected\r\nLANG_Status_NotConenct=Idle\r\nLANG_Loading_Config=Loading settings...\\n\r\nLANG_Welcome=osu!Live Sync ver {0:S} \r\nLANG_Help=Type 'help' command for help\r\nLANG_Command=Command\r\nLANG_Command_Description=Description\r\nLANG_Plugin_Cycle_Reference=The plugin is cycle referenced. {0:S} will not load without the dependent tree.\r\nLANG_IRC_Connecting=[Client] connecting...\r\nLANG_IRC_Disconnect=Disconnecting from IRC server...\r\nLANG_IRC_Connect_Timeout=Client connection failed,please check your internet connection.\r\nLANG_IRC_Ready=[Client] is ready!\r\nLANG_MsgMgr_Limit=Message limiter is toggled ON, the danmaku only with ?send prefix will be sent to irc\r\nLANG_MsgMgr_Free=Message limiter is toggled OFF.\r\nLANG_Instance_Exist=Sync is already started.\r\nLANG_COMMANDS_LOGIN=login <user> [pass] login to source to send message to source\r\nLANG_COMMANDS_EXIT=Stop sync and Exit\r\nLANG_COMMANDS_CLEAR=Clear output screen\r\nLANG_COMMANDS_STATUS=Get status of current Source and Client\r\nLANG_COMMANDS_STOP=Stop sync\r\nLANG_COMMANDS_START=Start sync\r\nLANG_COMMANDS_HELP=Print help message\r\nLANG_COMMANDS_SOURCEMSG=sourcemsg <message> Send a message to source for test\r\nLANG_COMMANDS_CLIENTMSG=clientmsg <message> Send a message to client for test\r\nLANG_COMMANDS_SOURCES=get all source list or set a source\r\nLANG_COMMANDS_MSGMGR=Execute with '--help' to get help\r\nLANG_COMMANDS_FILTERS=List all available filters.\r\nLANG_COMMANDS_DISABLE=disable <plugin name> Invoke Plugin OnDisable() function\r\nLANG_COMMANDS_SWITCH_CLIENT=switchclient [client] Switch to specific client\r\nLANG_COMMANDS_SOURCELOGIN=sourcelogin [user] [passwd] Login to source\r\nLANG_COMMANDS_RESTART=Restart Sync\r\nLANG_COMMANDS_LANG=lang [cultureName] Get/Set language\r\nLANG_COMMANDS_LISTLANG=listlang [--all] List (supported/all) languages\r\nLANG_COMMANDS_FILTERS_ITEM=Item \r\nLANG_COMMANDS_FILTERS_OBJ=Object\r\nLANG_COMMANDS_CLIENT_NAME=Client\r\nLANG_COMMANDS_CLIENT_AUTHOR=Author\r\nLANG_COMMANDS_SOURCES_NAME=Source\r\nLANG_COMMANDS_SOURCES_AUTHOR=Author\r\nLANG_COMMANDS_CURRENT=Set to {0:S}\r\nLANG_COMMANDS_DANMAKU_NOT_SUPPORT=Current source is not a Sendable source.\r\nLANG_COMMANDS_CHAT_IRC_NOTCONNECT=osu!irc not connect properly, can't send message.\r\nLANG_COMMANDS_DANMAKU_REQUIRE_LOGIN=Require Login to source!\r\nLANG_COMMANDS_START_ALREADY_RUN=An instance is already running\r\nLANG_COMMANDS_ARGUMENT_WRONG=Wrong argument.\r\nLANG_COMMANDS_MSGMGR_HELP=\\n--status \\n--limit <level> :Lower Level for less messaging\\n--option <type> :Auto/ForceAll/ForceLimit/DisableAll\r\nLANG_COMMANDS_MSGMGR_LIMIT=MessageManager is limited...\r\nLANG_COMMANDS_MSGMGR_FREE=MessageManager is freed...\r\nLANG_COMMANDS_MSGMGR_STATUS=MessageManager mode:{4:S},status:{0:D},queueCount/limitCount/recoverTime:{1}/{2}/{3}\r\nLANG_COMMANDS_MSGMGR_LIMIT_SPEED_SET=Set speed limit to {0}\r\nLANG_COMMANDS_MSGMGR_LIMIT_STYPE_SET=Set limit type to {0}\r\nLANG_COMMANDS_START_NO_SOURCE=need select a Source before start.\r\nLANG_COMMANDS_START_NO_CLIENT=need select a Client before start.\r\nLANG_COMMANDS_CURRENT_LANG=Current language: {0:S}\t{1:S}\r\nLANG_COMMANDS_LANG_SWITCHED=succesfully switch language to {1:S}({0:S})\r\nLANG_COMMANDS_LANG_NOT_FOUND=switch language fail! use 'listlang' show all available language\r\nLANG_UPDATE_DONE=Update done. Restart to complete update.\r\nLANG_INSTALL_DONE=Install done. Restart to complete install.\r\nLANG_PLUGIN_NOT_FOUND=Plugin {0} is not exist\r\nLANG_REMOVE_DONE=Remove done. Restart to apply effect\r\nLANG_VERSION_LATEST_OR_CANEL={0} is up-to-date or user caneled update operation.\r\nLANG_UPDATE_CHECK_ERROR=Cannot check update for [{0}] :  {1} : {2}\r\nLANG_UPDATE_ERROR=Cannot update :  {0} : {1}\r\nLANG_SOURCE_NOT_SUPPORT_SEND=The source {0} not support send message yet.\r\nLANG_NO_PLUGIN_SELECT=Must specify a plugin name\r\nLANG_PLUGIN_DISABLED=Disabled\r\nLANG_COMMANDS_CLIENTUSERMSG=chatuser <username> <message> send message as username for irc test\r\nLANG_COMMANDS_EXIT_DONE=Exit action execution done, please close the window.\r\nLANG_NO_ANY_SOURCE=\"There is no source available, please check Plugin folder or input 'plugins install DefaultPlugin' to install plugin for default sources.\"\r\n"
  },
  {
    "path": "Language/hu-HU/BanManagerPlugin.DefaultLanguage.lang",
    "content": "[hu-HU]\nLANG_HELP_BAN=ban user/id/regex , felhasznl kitiltsa, zenet kldve lesz IRC-re is\nLANG_HELP_UNBAN=unban user/id/regex , felhasznl tiltssnak feloldsa\nLANG_HELP_WHITELIST=add user/id/regex felhasznl engedlylisthoz adsa, akik tudnak IRC-re zenetet kldeni\nLANG_HELP_REMOVE_WHITELIST=remove user/id/regex felhasznl engedlylistrl val levtele\nLANG_HELP_ACCESS=hozzfrsi szint belltsa az IRC-en val zenetkldshez\nLANG_HELP_LIST=list whitelist(engedlylista) vagy banlist(tiltslista)\nLANG_ERR_COMMAND=parancs hiba\n"
  },
  {
    "path": "Language/hu-HU/DefaultGUI.Language.lang",
    "content": "[hu-HU]\nUI_DISPLAY=Fellet mutatsa\nUI_TIPS_BOTIRC=BotIRC\nUI_TIPS_BOTIRC_PASS=IRC Jelsz\nUI_TIPS_STATUS=Sttusz\nUI_TIPS_DANMAKU=LiveID\nUI_TIPS_OSU_IRC=osu!Chat\nUI_TIPS_TARGET_IRC=IRC cm\nUI_BOTTON_START=Indts\nUI_BOTTON_STOP=Lellts\nUI_BOTTON_LOGIN_DANMAKU=Platform bejelentkezs\nUI_BOTTON_SWITCH_CON=Vlts CMD felletre\nUI_BOTTON_EXIT=Kilps\nUI_TIPS_LIVE_ID=LiveID\nUI_TIPS_LIVE_SOURCE=Platform\nUI_INFO_RESTART_REQ=Platform megvltoztatshoz jraindts szksges. jraindtod most?"
  },
  {
    "path": "Language/hu-HU/DefaultPlugin.Language.lang",
    "content": "[hu-HU]\nLANG_COMMANDS_LOGIN=login <user> [pass] login to source to send message to source\nLANG_COMMANDS_EXIT=Folyamatok lelltsa s Kilps\nLANG_COMMANDS_CLEAR=Kperny tiszttsa\nLANG_COMMANDS_STATUS=Fut folyamatok sttusza\nLANG_COMMANDS_STOP=Jelenlegi folyamat lelltsa\nLANG_COMMANDS_START=Folyamat indtsa\nLANG_COMMANDS_HELP=Segtsg msolsa\nLANG_COMMANDS_SOURCEMSG=sourcemsg <message> Teszt ezenet kldse a platformnak\nLANG_COMMANDS_CLIENTMSG=clientmsg <message> Teszt ezenet kldse a kliensnek\nLANG_COMMANDS_SOURCES=sszes platform lekrdezse vagy belltsa\nLANG_COMMANDS_MSGMGR=Vgrehajts --help parancsal a sghoz\nLANG_COMMANDS_FILTERS=sszes szr listzsa.\nLANG_COMMANDS_DISABLE=disable <plugin name> Plugin funkcijnak kikapcsolsa\nLANG_COMMANDS_SWITCH_CLIENT=switchclient [client] Meghatrozott kliensre vlts\nLANG_COMMANDS_SOURCELOGIN=sourcelogin [user] [passwd] Bejelentkezs platformra\nLANG_COMMANDS_RESTART=Program jraindtsa\nLANG_COMMANDS_LANG=lang [cultureName] Nyelv lekrdezse/belltsa\nLANG_COMMANDS_LISTLANG=listlang [--all] Nyelvlista (tmogatott/sszes)\nLANG_COMMANDS_BILIBILI=setbili <roomID> Bilibili platform RoomID belltsa\nLANG_COMMANDS_FILTERS_ITEM=Trgy \nLANG_COMMANDS_FILTERS_OBJ=Objektum \nLANG_COMMANDS_SET_OSU_BOT=setosubot (botirc) (botirc_pw) (targetirc) OSUIRCBot Felhasznl belltsa\nLANG_COMMANDS_BOTIRC_CURRENT=Jelenlegi BotIRC: {0:S}\nLANG_COMMANDS_BOTIRC_SET=BotIRC jelenlegi belltsa erre: {0:S}\nLANG_COMMANDS_IRC_CURRENT=Clpont IRC: {0:S}\nLANG_COMMANDS_IRC_SET=Clpont IRC belltsa ide {0:S} \nLANG_COMMANDS_TARGET_CURRENT=Jelenlegi LiveID: {0:S}\nLANG_COMMANDS_TARGET_SET=Jelenlegi LiveID belltsa ide \nLANG_COMMANDS_CLIENT_NAME=Kliens\nLANG_COMMANDS_CLIENT_AUTHOR=Szerz\nLANG_COMMANDS_SOURCES_NAME=Platform\nLANG_COMMANDS_SOURCES_AUTHOR=Szerz\nLANG_COMMANDS_CURRENT=Bellts erre: {0:S}\nLANG_COMMANDS_DANMAKU_NOT_SUPPORT=Jelenlegi platform nem egy zenetkld platform.\nLANG_COMMANDS_EXIT_DONE=Kilps parancs vgrehajtva, krlek zrd be az ablakot.\nLANG_COMMANDS_CHAT_IRC_NOTCONNECT=osu! irc nincs csatlakozva, nem tudsz zenetet kldeni a kliensnek.\nLANG_COMMANDS_DANMAKU_REQUIRE_LOGIN=Platformra val bejelentkezs szksges!\nLANG_COMMANDS_START_ALREADY_RUN=Egy folyamat mr fut\nLANG_COMMANDS_ARGUMENT_WRONG=Rosszul megadott argument.\nLANG_COMMANDS_MSGMGR_HELP=\\n--status \\n--limit <level> :Kevesebb zenetkldshez cskkentsd a szintet\\n--option <type> :auto/force_all/force_limit\nLANG_COMMANDS_MSGMGR_LIMIT=Korltozott...\nLANG_COMMANDS_MSGMGR_FREE=Korltlan\nLANG_COMMANDS_MSGMGR_STATUS=MessageManager md:{4:S},sttusz:{0:D},queueCount/limitCount/recoverTime:{1}/{2}/{3}\nLANG_COMMANDS_MSGMGR_LIMIT_SPEED_SET=Sebessg hatr belltsa erre: {0}\nLANG_COMMANDS_MSGMGR_LIMIT_STYPE_SET=Tpus hatr belltsa erre: {0}\nLANG_SEND_COOKIE_SAVED=Bejelentkezsi informci sikeresen mentve\nLANG_SEND_DONE=zenet elkldve.\nLANG_DOUYU_FAIL=Kapcsolat ellenrzse sikertelen! Except {0}, Result {1}\nLANG_DOUYU_AUTH_SUCC=Douyutv bejelentkezs sikeres\nLANG_DOUYU_DANMAKU=Fogadott zenet: {0}:{1}\nLANG_DOUYU_GIFT=Ajndk\nLANG_BILIBILI_ONLINECHANGE=Online vltozs: {0}\nLANG_COMMANDS_CLIENTUSERMSG=chatuser <username> <message> IRC zenet kldse lbe egy megadott felhasznlnak\n"
  },
  {
    "path": "Language/hu-HU/NowPlaying.Languages.lang",
    "content": "[hu-HU]\nOSU_PATH_NOT_SET=osu! megadott elrsi helye res, a program prblja megkeresni a fut osu! alkalmazst.\nFIND_OSU_PATH=osu! elrsi helye nem tallhat! {0:S}\nOSU_PATH_FAIL=osu! elrsi helye nem tallhat, Rszletes informcik letiltva.\nERROR_WHILE_FIND_PATH=Hiba trtnt az osu! elrsi helynek keressekor {0:S}\nUNKNOWN_COMMAND=Ismeretlen parancs {0:S}\nSTATUS_PLAYING=Jtkban\nSTATUS_EDITING=Szerkeszt\nSTATUS_OTHER=Hallgatja\nSTATUS_TIP_INFO=n vagyok {0:S}{1:S}\nSTATUS_TIP_INFO_WRAP=n vagyok {0:S}{1:S}\nERROR_WHILE_SEARCH_MAP=Hiba keress kzben {0:S}-{1:S} [{2:S}] {3:S}\nCONSOLE_OUTPUT_RESULT=Fjl:{0:S} ({1}ms) HP/CS/AR/OD: {2}{3}{4}{5}\nOUTPUT_RESULT=Jelenlegi {0}:{1}\nCURRENT_IDLE=Jelenleg ttlen"
  },
  {
    "path": "Language/hu-HU/OsuRTDataProvider.DefaultLanguage.lang",
    "content": "[hu-HU]\nLANG_OSU_NOT_FOUND=[OsuRTDataProvider][ID:{0} osu!.exe folyamat nem tallhat\nLANG_OSU_FOUND=[OsuRTDataProvider][ID:{0}]osu!.exe folyamat szlelve\nLANG_INIT_PLAY_FINDER_FAILED=[OsuRTDataProvider][ID:{0}]Init PlayFinder Hiba! jraprblkozs {1} msodperc mlva\nLANG_INIT_PLAY_FINDER_SUCCESS=[OsuRTDataProvider][ID:{0}]Init PlayFinder Sikerlt!\nLANG_INIT_STATUS_FINDER_FAILED=[OsuRTDataProvider][ID:{0}]Init StatusFinder Hiba! jraprblkozs {1} msodperc mlva\nLANG_INIT_STATUS_FINDER_SUCCESS=[OsuRTDataProvider][ID:{0}]Init StatusFinder Sikerlt!\nLANG_INIT_BEATMAP_FINDER_FAILED=[OsuRTDataProvider][ID:{0}]Init BeatmapFinder Hiba! jraprblkozs {1} msodperc mlva\nLANG_INIT_BEATMAP_FINDER_SUCCESS=[OsuRTDataProvider][ID:{0}]Init BeatmapFinder Sikerlt!\nLANG_INIT_MODE_FINDER_FAILED=[OsuRTDataProvider][ID:{0}]Init ModeFinder Hiba! jraprblkozs {1} msodperc mlva\nLANG_INIT_MODE_FINDER_SUCCESS=[OsuRTDataProvider][ID:{0}]Init ModeFinder Sikerlt!\nLANG_BEATMAP_NOT_FOUND=Beatmap nem tallhat"
  },
  {
    "path": "Language/hu-HU/RealTimePPDisplayer.DefaultLanguage.lang",
    "content": "[hu-HU]\nUI_MENU_TOPMOST=Legnagyobb\nTEXT_MODE_OUTPUT_PATH_FORMAT=[RealTimePPDisplayer]PP File: {0}\nMMF_MODE_OUTPUT_PATH_FORMAT=[RealTimePPDisplayer]Memory Mapping File: {0}\n"
  },
  {
    "path": "Language/hu-HU/RecentlyUserQuery.DefaultLanguage.lang",
    "content": "[hu-HU]\nLANG_HELP=\\nEz egy sg az osu!irc ltalnos parancsaihoz(osu!irc-ben krlek hasznlj \"?\" eltagot a parancso keltt)\\nrecently --status | Jelenlegi zenetelzmny sttusznak lekrdezse (osu!irc nem lehetsges)\\nrecently --u <userName> | <userName> elzmnynek lekrdezse (Nem ajnlott osu!irc hasznlata)\\nrecently --i <userId> | <userId> elzmnynek lekrdezse (Nem ajnlott osu!irc hasznlata)\\nrecently | Felhasznlnv s ID lekrdezse, az ID felhasznlhat parancsok vgrehajtshoz, pldul \"?ban --i\" (osu!irc tmogatott)\\nrecently --disable | zenetek mentsnek kikapcsols, sszes zenetelzmny trlve (osu!irc tmogatott)\\nrecently --start | zenetek mentsnek elkezdse (osu!irc tmogatott)\\nrecently --realloc <count> | zenetelzmnyek trhelynek jraellenrzse tbb zenet trolshoz (osu!irc tmogatott)\\nrecently --recently | Legutbbi felhasznl ID lekrdezse\\n\nLANG_MSG_STATUS=MessageRecord sttusz: {0} | recordCount/Capacity : {1}/{2}\nLANG_RUNNING=fut\nLANG_STOP=lelltott\nLANG_MSG_DISABLE=zenetek mentse letiltva, az sszes trolt zenet elzmny trlve\nLANG_MSG_START=zenetek mentse elkezddtt\nLANG_MSG_REALLOC_ERR=MessageRecord: Rossz parancs\nLANG_MSG_REALLOC=zenetek lehetsges trolsi ideje: {0}\nLANG_MSG_NOTIMPLENT=Nincs beptve\nLANG_MSG_UNKNOWNCOMMAND=Ismeretlen parancs."
  },
  {
    "path": "Language/hu-HU/Sync.Tools.DefaultI18n.lang",
    "content": "[hu-HU]\nLANG_Loading=Betlts...\nLANG_Plugins=Betltve {0:D} Plugin\nLANG_Sources=Betltve {0:D} Platform\nLANG_Client=Betltve {0:D} Kliens\nLANG_Error=Warpper nem tlthet be! ELLENRIZD A PLUGINOKAT.\nLANG_Commands=Betltve {0:D} Parancs\nLANG_Filters=Betltve {0:D} Szr\nLANG_Ready=Ksz\nLANG_RqueireLogin=Krlek lpj be a fikodba\nLANG_AccountName=Felhasznlnv:\nLANG_AccountPw=Jelsz:\nLANG_AccountSave=Fik sikeresen mentve!\nLANG_Start=Indts...\nLANG_Stopping=Lellts...\nLANG_Restarting=jraindts...\nLANG_LoadingPlugin={0:S} betltse...\nLANG_LoadPluginErr=Nem lehet betlteni {0:S} ({1:S})\nLANG_NotPluginErr={0:S} nem egy ltalnos plugin ({1:S})\nLANG_NotConfig=Krlek szerkeszd a 'config.ini'-t tovbbi belltsokrt.\nLANG_NoSource=Warpper platform nem tallhat. Krlek telepts egy platformot.\nLANG_MissSource=A megadott belltsokkal platform nem tallhat.\nLANG_SetSource={0:S} belltsa jelenlegi l platformnak \nLANG_SupportSend=Bejelentkezs utn a platformon keresztl tudsz zenetet kldeni (Parancs: login [user] [passwd])\nLANG_CertLength=Bizonylat hosszsga: {0:D}\nLANG_CertExist=Jelenlegi bizonylat mr ltezik, bejelentkezsi paranccsal fellrhat\nLANG_SendNotReady=Jelenlegi paltform nem ll kszen zenet kldsre.\nLANG_UnknowCommand=Ismeretlen parancs, rj 'help' parancsot a sghoz.\nLANG_CommandFail=Parancs vgrehajtsi hiba, rj 'help' parancsot a sghoz.\nLANG_ConfigFile=Belltsok\nLANG_UserCount=Felhasznl szm vltozs : {0:D}\nLANG_UserCount_Change=Megfigyelk {0:S} to {1:D}\nLANG_UserCount_Change_Increase=nvels\nLANG_UserCount_Change_Decrease=cskkents\nLANG_Source_Disconnecting=Platform kapcsolat lecsatlakozs...\nLANG_Source_Disconnected=Lecsatlakozva a platform szerverrl. jracsatlakozs 3 msodperc mlva.\nLANG_Source_Disconnected_Succ=Sikeresen lecsatlakoztl a platform szerverrl.\nLANG_Source_Connect=Csatlakozs a platform szervehez...\nLANG_Source_Connected_Succ=Sikeres csatlakozs  apaltform szerverhez\nLANG_Current_Online=Jelenleg elrhet : {0:D}\nLANG_Gift_Sent=Kldk {O:D} {1:S} neked!\nLANG_Config=Belltsok: \nLANG_Config_Status_OK=OK, LiveID:{0}\nLANG_Config_Status_Fail=Konfigurls sikertelen.\nLANG_Source=Platform{0:S}: \nLANG_IRC=Kliens:\nLANG_Danmaku=zenet kldse:\nLANG_Status_Connected=Csatlakozva\nLANG_Status_NotConenct=Ttlen\nLANG_Loading_Config=Belltsok betltse...\\n\nLANG_Welcome=osu!Live Sync vezi {0:S} \nLANG_Help=rj 'help' parancsot a sgrt\nLANG_Command=Parancs\nLANG_Command_Description=Lers\nLANG_Plugin_Cycle_Reference=A plugin ciklushoz kttt. {0:S} megfelel gazata nlkl nem fog betlteni.\nLANG_IRC_Connecting=[Client] csatlakozs...\nLANG_IRC_Disconnect=Lecsatlakozs az IRC szerverrl...\nLANG_IRC_Connect_Timeout=Kliens csatlakozs sikertelen, krlek ellenrizd az internet kapcsolatodat.\nLANG_IRC_Ready=[Client] kszen ll!\nLANG_MsgMgr_Limit=zenet korltoz BEKAPCSOLVA, az zenet csak ?send eltaggal lesz elkldve IRC-re\nLANG_MsgMgr_Free=zenet korltoz KIKAPCSOLVA.\n\n"
  },
  {
    "path": "Language/ja-JP/BanManagerPlugin.DefaultLanguage.lang",
    "content": "[ja-JP]\nLANG_HELP_BAN=ban user/id/regex ,bZ[WIRCɑM܂\nLANG_HELP_UNBAN=unban user/id/regex\nLANG_HELP_WHITELIST=add user/id/regex IRCɃbZ[W𑗐MłzCgXgɒǉ܂\nLANG_HELP_REMOVE_WHITELIST=remove user/id/regex ŃzCgXg폜\nLANG_HELP_ACCESS=IRCɃbZ[W𑗐M邽߂̃ANZXxݒ肷\nLANG_HELP_LIST=zCgXgEBANXg\nLANG_ERR_COMMAND=R}hG[\n"
  },
  {
    "path": "Language/ja-JP/BeatmapSuggest.DefaultLanguage.lang",
    "content": "[ja-JP]\nLANG_GET_BEATMAP_FAILED={0} r[g}bv̏̎擾Ɏs܂BG[bZ[W: {1}\nLANG_SUGGEST_MEG={0} r[g}bvvCꍇ [{1} {2}] || [{3} dl] || [{4} mirror]  ?dl  ?dl all Ɠ͂Ă\nLANG_NOT_FOUND_ERR=beatmapSetID̏ƈvȂp[^[IDbeatmapSetIDł\nLANG_UNKNOWN_TITLE=<s̃^Cg>\nLANG_GET_BEATMAP_TIME_OUT=r[g}bv擾܂ {0}b^CAEg ^XNXe[^X: {1}\nLANG_INVAILD_ID={0} ͖IDł\nLANG_UNKOWN_PARAM={0} p[^[͌܂ł\nLANG_ERROR_INFO_IMCOMPLETE=sSȏ\nLANG_NO_API_KEY_NOFITY=config.iniosu!APIL[܂Bgp邽߂osu!APIL[ݒ肵Ă (osu!APIL[͂擾ł܂: https://osu.ppy.sh/p/api )\nLANG_START_DOWNLOAD=r[g}bṽ_E[hJn܂: {0}\nLANG_FINISH_DOWNLOAD={0} r[g}bv_E[h܂\nLANG_FAILED_DOWNLOAD={0} ̃_E[hɎs܂BG[bZ[W: {1}\nLANG_DOWNLOAD_TASK_COUNT={0} ̃r[g}bṽ_E[hJn܂\n"
  },
  {
    "path": "Language/ja-JP/ConfigGUI.DefaultLanguage.lang",
    "content": "[ja-JP]\nBUTTON_OPEN=J\nBUTTON_BROWSE=Q\nBUTTON_FONT=tHg\nBUTTON_COLOR=F\nWINDOW_TITLE=ݒ\nBUTTON_SAVE=ۑ\nLABEL_SAVED=ۑ܂I\nLABEL_SAVED_SAVING=ۑ...\nWINDOW_TITLE_REQUIRE_RESTART=ݒLɂɂ͍ċNĂ\n"
  },
  {
    "path": "Language/ja-JP/DefaultGUI.Language.lang",
    "content": "[ja-JP]\nUI_DISPLAY=UI\\\nUI_TIPS_BOTIRC=BotIRC\nUI_TIPS_BOTIRC_PASS=IRC pX[h\nUI_TIPS_STATUS=Xe[^X\nUI_TIPS_DANMAKU=LiveID\nUI_TIPS_OSU_IRC=osu!Chat\nUI_TIPS_TARGET_IRC=IRC\nUI_BOTTON_START=Jn\nUI_BOTTON_STOP=~\nUI_BOTTON_LOGIN_DANMAKU=\\[XɃOC\nUI_BOTTON_SWITCH_CON=R}hUIɐ؂ւ\nUI_BOTTON_EXIT=I\nUI_TIPS_LIVE_ID=LiveID\nUI_TIPS_LIVE_SOURCE=\\[X\nUI_INFO_RESTART_REQ=SyncċNƃCu\\[X̕ύXKp܂B ċN܂H\n"
  },
  {
    "path": "Language/ja-JP/DefaultPlugin.Language.lang",
    "content": "[ja-JP]\nLANG_COMMANDS_LOGIN=login <[U[> [pX[h] Ń\\[XɃOCăbZ[W𑗐M܂\nLANG_COMMANDS_EXIT=Sync~ďI܂\nLANG_COMMANDS_CLEAR=\\Ă郍OZbg܂\nLANG_COMMANDS_STATUS=\\[XƃNCAǧ݂̃Xe[^X擾܂\nLANG_COMMANDS_STOP=Sync~܂\nLANG_COMMANDS_START=SyncJn܂\nLANG_COMMANDS_HELP=wv\\܂\nLANG_COMMANDS_SOURCEMSG=sourcemsg <bZ[W> Ń\\[XɃeXgbZ[W𑗐M܂\nLANG_COMMANDS_CLIENTMSG=clientmsg <bZ[W> ŃNCAgɃeXgbZ[W𑗐M܂\nLANG_COMMANDS_SOURCES=ׂẴ\\[X̃Xg\\邩A\\[X̐ݒ܂\nLANG_COMMANDS_MSGMGR=--help s邱ƂŃwv\\܂\nLANG_COMMANDS_FILTERS=gp\\̃tB^[Xgɂĕ\\܂\nLANG_COMMANDS_DISABLE=disable <vOC> ŃvOCOnDisable()֐Ăяo܂\nLANG_COMMANDS_SWITCH_CLIENT=switchclient [NCAg] ŃNCAg؂ւ܂\nLANG_COMMANDS_SOURCELOGIN=sourcelogin [[U[] [pX[h] Ń\\[XɃOC܂\nLANG_COMMANDS_RESTART=SyncċN܂\nLANG_COMMANDS_LANG=lang [ꖼ (: ja-JP)]  ύX܂\nLANG_COMMANDS_LISTLANG=listlang [--all] Ή/ׂĂ̌̃Xg\\܂\nLANG_COMMANDS_BILIBILI=setbili <roomID> BiliBili\\[XRoomIDݒ肵܂\nLANG_COMMANDS_FILTERS_ITEM=ACe \nLANG_COMMANDS_FILTERS_OBJ=IuWFNg\nLANG_COMMANDS_SET_OSU_BOT=setosubot (botirc) (botirc_pw) (targetirc) OSUIRC BotAJEgݒ肵܂\nLANG_COMMANDS_BOTIRC_CURRENT=݂BotIRC: {0:S}\nLANG_COMMANDS_BOTIRC_SET=BotIRC {0:S} ɐݒ肵܂\nLANG_COMMANDS_IRC_CURRENT=݂̃^[QbgIRC: {0:S}\nLANG_COMMANDS_IRC_SET=^[QbgIRC {0:S} ɕύX܂\nLANG_COMMANDS_TARGET_CURRENT=݂LiveID: {0:S}\nLANG_COMMANDS_TARGET_SET=LiveIDύX܂\nLANG_COMMANDS_CLIENT_NAME=NCAg\nLANG_COMMANDS_CLIENT_AUTHOR=\nLANG_COMMANDS_SOURCES_NAME=\\[X\nLANG_COMMANDS_SOURCES_AUTHOR=\nLANG_COMMANDS_CURRENT={0:S} ɐݒ\nLANG_COMMANDS_DANMAKU_NOT_SUPPORT=݂̃\\[Xł͑Mł܂\nLANG_COMMANDS_CHAT_IRC_NOTCONNECT=osu!IRCɐڑłȂ߃bZ[W𑗐Mł܂\nLANG_COMMANDS_DANMAKU_REQUIRE_LOGIN=\\[XɃOCĂI\nLANG_COMMANDS_START_ALREADY_RUN=CX^X͂łɎsĂ܂\nLANG_COMMANDS_ARGUMENT_WRONG=Ⴂ܂\nLANG_COMMANDS_MSGMGR_HELP=\\n--status \\n--limit <x> :ႢxɂƃbZ[W点܂ \\n--option <^Cv> : Auto/ForceAll/ForceLimit/DisableAll\nLANG_COMMANDS_MSGMGR_LIMIT=bZ[W}l[W[͐Ă܂...\nLANG_COMMANDS_MSGMGR_FREE=bZ[W}l[W[͊JĂ܂...\nLANG_COMMANDS_MSGMGR_STATUS=bZ[W}l[W[ [h: {4:S}, Xe[^X: {0:D}, L[̐/̐/񕜎: {1} / {2} / {3}\nLANG_COMMANDS_MSGMGR_LIMIT_SPEED_SET=x {0} ɕύX\nLANG_COMMANDS_MSGMGR_LIMIT_STYPE_SET=̃^Cv {0} ɕύX\nLANG_COMMANDS_START_NO_SOURCE=JnOɃ\\[XIKv܂\nLANG_COMMANDS_START_NO_CLIENT=JnOɃNCAgIKv܂\nLANG_COMMANDS_CURRENT_LANG=݂̌: {0:S}\t{1:S}\nLANG_COMMANDS_LANG_SWITCHED= {1:S}({0:S}) ɐ؂ւ܂\nLANG_COMMANDS_LANG_NOT_FOUND=̐؂ւɎs܂I listlang R}hŌݗpł錾̃Xg\\ł܂\nLANG_SEND_COOKIE_SAVED=OC񂪕ۑ܂\nLANG_SEND_DONE=bZ[WM܂\nLANG_DOUYU_FAIL=ڑXe[^X̊mFɎs܂I O: {0} : {1}\nLANG_DOUYU_AUTH_SUCC=DouyutvF؂ɐ\nLANG_DOUYU_DANMAKU=bZ[WM: {0}:{1}\nLANG_DOUYU_GIFT=Mtg\nLANG_BILIBILI_ONLINECHANGE=IC̕ύX: {0}\nLANG_OSUIRC_LOGIN_SUCCESS=OCɐ\nLANG_OSUIRC_LOGIN_FAILED=osu!IRC̍ĐڑɎs܂B sR: {0}\nLANG_OSUIRC_NETWORK_INTERRUPTED=lbg[Nf܂BĐڑĂ\nLANG_OSUIRC_RECIVER_FINISHED=MXbhI܂\nLANG_COMMANDS_CLIENTUSERMSG=chatuser <[U[> <bZ[W> IRCeXg̃[U[ŃbZ[W𑗐M܂\nLANG_COMMANDS_EXIT_DONE=I܂BEBhEĂ\n"
  },
  {
    "path": "Language/ja-JP/NowPlaying.Languages.lang",
    "content": "[ja-JP]\nOSU_PATH_NOT_SET=osu!̃pXݒ肳Ă܂Asosu!̃CX^X܂\nFIND_OSU_PATH=osu!̃pX邱Ƃł܂I {0:S}\nOSU_PATH_FAIL=osu!̃pX邱Ƃł܂Aڍ׏̕\\͖ɂȂĂ܂B\nERROR_WHILE_FIND_PATH=osu!̃pXɃG[܂ {0:S}\nUNKNOWN_COMMAND=R}h܂ {0:S}\nSTATUS_PLAYING=vC\nSTATUS_EDITING=ҏW\nSTATUS_OTHER=Đ\nSTATUS_TIP_INFO= {0:S}{1:S}\nSTATUS_TIP_INFO_WRAP= {0:S}{1:S}\nERROR_WHILE_SEARCH_MAP={0:S}-{1:S} [{2:S}] {3:S} ̌ɃG[܂\nCONSOLE_OUTPUT_RESULT=t@C:{0:S} ({1}ms) HP/CS/AR/OD: {2} / {3} / {4} / {5}\nOUTPUT_RESULT= {0}:{1}\nCURRENT_IDLE=ݑҋ@\n"
  },
  {
    "path": "Language/ja-JP/OsuRTDataProvider.DefaultLanguage.lang",
    "content": "[ja-JP]\nLANG_OSU_NOT_FOUND=[OsuRTDataProvider][ID:{0}]osu!.exevZX܂ł\nLANG_OSU_FOUND=[OsuRTDataProvider][ID:{0}]osu!.exevZX܂\nLANG_INIT_PLAY_FINDER_FAILED=[OsuRTDataProvider][ID:{0}]PlayFindeȑɎs܂I {1} bɍĎs܂\nLANG_INIT_PLAY_FINDER_SUCCESS=[OsuRTDataProvider][ID:{0}]PlayFindeȑɐ܂I\nLANG_INIT_STATUS_FINDER_FAILED=[OsuRTDataProvider][ID:{0}]StatusFindeȑɎs܂I {1} bɍĎs܂\nLANG_INIT_STATUS_FINDER_SUCCESS=[OsuRTDataProvider][ID:{0}]StatusFindeȑɐ܂I\nLANG_INIT_BEATMAP_FINDER_FAILED=[OsuRTDataProvider][ID:{0}]BeatmapFindeȑɎs܂I {1} bɍĎs܂\nLANG_INIT_BEATMAP_FINDER_SUCCESS=[OsuRTDataProvider][ID:{0}]BeatmapFindeȑɐ܂I\nLANG_INIT_MODE_FINDER_FAILED=[OsuRTDataProvider][ID:{0}]ModeFindeȑɎs܂I {1} bɍĎs܂\nLANG_INIT_MODE_FINDER_SUCCESS=[OsuRTDataProvider][ID:{0}]ModeFindeȑɐ܂I\nLANG_BEATMAP_NOT_FOUND=r[g}bv܂"
  },
  {
    "path": "Language/ja-JP/RealTimePPDisplayer.DefaultLanguage.lang",
    "content": "[ja-JP]\nUI_MENU_TOPMOST=ŏ\nTEXT_MODE_OUTPUT_PATH_FORMAT=[RealTimePPDisplayer]PP t@C: {0}\nMMF_MODE_OUTPUT_PATH_FORMAT=[RealTimePPDisplayer][}bsOt@C: {0}\n"
  },
  {
    "path": "Language/ja-JP/RecentlyUserQuery.DefaultLanguage.lang",
    "content": "[ja-JP]\nLANG_HELP=\\nosu!IRC̃R}hwvł(osu!IRCł̓R}h̐擪 ? ǉĂ)\\nrecently --status | ݂̃Xe[^XO擾܂(osu!IRCł͎gps)\\nrecently --u <[U[> | <[U[>̗擾܂(osu!IRCł̎gp͔񐄏)\\nrecently --i <[U[ID> | <[U[ID>̗擾܂(osu!IRCł̎gp͔񐄏)\\nrecently | ŋ߂̃[U[ID擾AIDgp ?ban --i Ȃǂ̃R}hsł܂(osu!IRCɑΉ)\\nrecently --disable | bZ[WȌo͂𖳌ɂAׂẴO폜܂(osu!IRCɑΉ)\\nrecently --start | ēxȌo͂Jn܂(osu!IRCɑΉ)\\nrecently --realloc <JEg> | O̗eʂĊ蓖ĂăOۑeʂ𑽂܂(osu!IRCɑΉ)\\nrecently --recently | ŋ߂̃[U[ID擾܂\\n\nLANG_MSG_STATUS=bZ[WL^̃Xe[^X: {0} | L^/e : {1}/{2}\nLANG_RUNNING=ғ\nLANG_STOP=~\nLANG_MSG_DISABLE=bZ[WȌo͂ɂȂ肷ׂẴO܂\nLANG_MSG_START=bZ[WȌo͂Jn܂\nLANG_MSG_REALLOC_ERR=bZ[WL^: ߂ɊԈႢ܂\nLANG_MSG_REALLOC={0} ̃bZ[WOɏo͂ł悤ɂȂ܂\nLANG_MSG_NOTIMPLENT=Ă܂\nLANG_MSG_UNKNOWNCOMMAND=R}h܂"
  },
  {
    "path": "Language/ja-JP/Sync.Tools.DefaultI18n.lang",
    "content": "[ja-JP]\nLANG_Loading=Ǎ...\nLANG_Plugins={0:D} ̃vOCǂݍ݂܂\nLANG_Sources={0:D} ̃\\[Xǂݍ݂܂\nLANG_Client={0:D} ̃NCAgǂݍ݂܂\nLANG_Error=bp[̏ł܂I vOCmFĂI\nLANG_Commands={0:D} ̃R}hǂݍ݂܂\nLANG_Filters={0:D} ̃tB^[ǂݍ݂܂\nLANG_Ready=I\nLANG_RqueireLogin=OCĂ\nLANG_AccountName=[U[:\nLANG_AccountPw=pX[h:\nLANG_AccountSave=AJEgۑ܂I\nLANG_Start=N...\nLANG_Stopping=~...\nLANG_Restarting=ċN...\nLANG_LoadingPlugin={0:S} Ǎ...\nLANG_LoadPluginErr={0:S} ǂݍ݂ł܂ł ({1:S})\nLANG_NotPluginErr={0:S} ͒ʏ̃vOCł͂܂ ({1:S})\nLANG_NotConfig=config.ini ҏWAݒĂ\nLANG_NoSource=\\[XɃbp[܂łB\\[XCXg[Ă\nLANG_MissSource=ݒ肩\\[X邱Ƃł܂ł\nLANG_SetSource=Cu\\[X {0:S} ɕύX܂\nLANG_SupportSend=OC\\[X烁bZ[W𑗂邱Ƃł܂ (R}h: login [[U[] [pX[h])\nLANG_CertLength=F؊: {0:D}\nLANG_CertExist=F؂łɑ݂܂Blogin R}hŏ㏑ĂB\nLANG_SendNotReady=݂̃\\[Xł͒e𑗐M鏀łĂ܂\nLANG_UnknowCommand=R}h܂Bhelp Ɠ͂ƃR}hXg\\邱Ƃł܂\nLANG_CommandFail=R}hsɎs܂Bhelp Ɠ͂ƃR}hXg\\邱Ƃł܂\nLANG_ConfigFile=ݒ\nLANG_UserCount=[U[̕ύX : {0:D}\nLANG_UserCount_Change=XyN^[ {0:S}  {1:D} ɂȂ܂\nLANG_UserCount_Change_Increase=\nLANG_UserCount_Change_Decrease=\nLANG_Source_Disconnecting=\\[X̐ڑؒfĂ܂...\nLANG_Source_Disconnected=\\[XT[o[ؒf܂B 3bɍĐڑ܂\nLANG_Source_Disconnected_Succ=\\[XT[o[ؒf܂\nLANG_Source_Connect=\\[XT[o[ɐڑ...\nLANG_Source_Connected_Succ=\\[XT[o[ɐڑ܂I\nLANG_Current_Online=݂̃IC : {0:D}\nLANG_Gift_Sent=Ȃ {O:D}  {1:S} 𑗂܂I\nLANG_Config=ݒ: \nLANG_Config_Status_OK=OK, LiveID:{0}\nLANG_Config_Status_Fail=s܂\nLANG_Source=\\[X {0:S}: \nLANG_IRC=NCAg:\nLANG_Danmaku=eM:\nLANG_Status_Connected=ڑ\nLANG_Status_NotConenct=ҋ@\nLANG_Loading_Config=ݒǍ...\\n\nLANG_Welcome=osu!Live Sync o[W: {0:S} \nLANG_Help=help Ɠ͂ƃwv\\邱Ƃł܂\nLANG_Command=R}h\nLANG_Command_Description=\nLANG_Plugin_Cycle_Reference=vOC͏zQƂ܂B {0:S} ͈ˑc[Ȃƃ[h܂\nLANG_IRC_Connecting=[NCAg] ڑ...\nLANG_IRC_Disconnect=IRCT[o[ؒf...\nLANG_IRC_Connect_Timeout=NCAg̐ڑɎs܂BC^[lbg̐ڑmFĂ\nLANG_IRC_Ready=[NCAg] I\nLANG_MsgMgr_Limit=bZ[WIɐ؂ւ܂B?send vtBbNX܂܂ꂽêIRCɑM܂\nLANG_MsgMgr_Free=bZ[WItɐ؂ւ܂B\nLANG_Instance_Exist=Sync͂łɋNĂ܂\nLANG_COMMANDS_LOGIN=login <[U[> [pX[h] Ń\\[XɃOCăbZ[W𑗐M܂\nLANG_COMMANDS_EXIT=Sync~ďI܂\nLANG_COMMANDS_CLEAR=\\Ă郍OZbg܂\nLANG_COMMANDS_STATUS=\\[XƃNCAǧ݂̃Xe[^X擾܂\nLANG_COMMANDS_STOP=Sync~܂\nLANG_COMMANDS_START=SyncJn܂\nLANG_COMMANDS_HELP=wv\\܂\nLANG_COMMANDS_SOURCEMSG=sourcemsg <bZ[W> Ń\\[XɃeXgbZ[W𑗐M܂\nLANG_COMMANDS_CLIENTMSG=clientmsg <bZ[W> ŃNCAgɃeXgbZ[W𑗐M܂\nLANG_COMMANDS_SOURCES=ׂẴ\\[X̃Xg\\邩A\\[X̐ݒ܂\nLANG_COMMANDS_MSGMGR=--help s邱ƂŃwv\\܂\nLANG_COMMANDS_FILTERS=gp\\̃tB^[Xgɂĕ\\܂\nLANG_COMMANDS_DISABLE=disable <vOC> ŃvOCOnDisable()֐Ăяo܂\nLANG_COMMANDS_SWITCH_CLIENT=switchclient [NCAg] ŃNCAg؂ւ܂\nLANG_COMMANDS_SOURCELOGIN=sourcelogin [[U[] [pX[h] Ń\\[XɃOC܂\nLANG_COMMANDS_RESTART=SyncċN܂\nLANG_COMMANDS_LANG=lang [ꖼ (: ja-JP)]  ύX܂\nLANG_COMMANDS_LISTLANG=listlang [--all] Ή/ׂĂ̌̃Xg\\܂\nLANG_COMMANDS_FILTERS_ITEM=ACe \nLANG_COMMANDS_FILTERS_OBJ=IuWFNg\nLANG_COMMANDS_CLIENT_NAME=NCAg\nLANG_COMMANDS_CLIENT_AUTHOR=\nLANG_COMMANDS_SOURCES_NAME=\\[X\nLANG_COMMANDS_SOURCES_AUTHOR=\nLANG_COMMANDS_CURRENT={0:S} ɐݒ\nLANG_COMMANDS_DANMAKU_NOT_SUPPORT=݂̃\\[Xł͑Mł܂\nLANG_COMMANDS_CHAT_IRC_NOTCONNECT=osu!IRCɐڑłȂ߃bZ[W𑗐Mł܂\nLANG_COMMANDS_DANMAKU_REQUIRE_LOGIN=\\[XɃOCĂI\nLANG_COMMANDS_START_ALREADY_RUN=CX^X͂łɎsĂ܂\nLANG_COMMANDS_ARGUMENT_WRONG=Ⴂ܂\nLANG_COMMANDS_MSGMGR_HELP=\\n--status \\n--limit <x> :ႢxɂƃbZ[W点܂ \\n--option <^Cv> : Auto/ForceAll/ForceLimit/DisableAll\nLANG_COMMANDS_MSGMGR_LIMIT=bZ[W}l[W[͐Ă܂...\nLANG_COMMANDS_MSGMGR_FREE=bZ[W}l[W[͊JĂ܂...\nLANG_COMMANDS_MSGMGR_STATUS=bZ[W}l[W[ [h: {4:S}, Xe[^X: {0:D}, L[̐/̐/񕜎: {1} / {2} / {3}\nLANG_COMMANDS_MSGMGR_LIMIT_SPEED_SET=x {0} ɕύX\nLANG_COMMANDS_MSGMGR_LIMIT_STYPE_SET=̃^Cv {0} ɕύX\nLANG_COMMANDS_START_NO_SOURCE=JnOɃ\\[XIKv܂\nLANG_COMMANDS_START_NO_CLIENT=JnOɃNCAgIKv܂\nLANG_COMMANDS_CURRENT_LANG=݂̌: {0:S}\t{1:S}\nLANG_COMMANDS_LANG_SWITCHED= {1:S}({0:S}) ɐ؂ւ܂\nLANG_COMMANDS_LANG_NOT_FOUND=̐؂ւɎs܂I listlang R}hŌݗpł錾̃Xg\\ł܂\nLANG_UPDATE_DONE=Abvf[g܂BċNƃAbvf[gK܂\nLANG_INSTALL_DONE=CXg[܂BċNƃCXg[K܂\nLANG_PLUGIN_NOT_FOUND={0} vOC݂܂\nLANG_REMOVE_DONE=폜܂BKɂ͍ċNĂ\nLANG_VERSION_LATEST={0} ͍ŐVł\nLANG_UPDATE_CHECK_ERROR=[{0} ̃o[W̍XVmFł܂] :  {1} : {2}\nLANG_UPDATE_ERROR=XVł܂ :  {0} : {1}\nLANG_SOURCE_NOT_SUPPORT_SEND={0} \\[X͂܂bZ[WMɑΉĂ܂\nLANG_NO_PLUGIN_SELECT=vOCw肷Kv܂\nLANG_PLUGIN_DISABLED=\nLANG_COMMANDS_CLIENTUSERMSG=chatuser <[U[> <bZ[W> IRCeXg̃[U[ŃbZ[W𑗐M܂\nLANG_COMMANDS_EXIT_DONE=I܂BEBhEĂ\nLANG_NO_ANY_SOURCE=ugp\\ȃvOC܂BvOCtH_[mF邩Aplugins install DefaultPlugin Ɠ͂ăftHg̃vOCCXg[ĂBv\n"
  },
  {
    "path": "Language/ru-RU/BanManagerPlugin.DefaultLanguage.lang",
    "content": "[ru-RU]\nLANG_HELP_BAN= /id/regex ,    irc\nLANG_HELP_UNBAN= /id/regex\nLANG_HELP_WHITELIST= /id/regex   ,       irc\nLANG_HELP_REMOVE_WHITELIST= /id/regex   \nLANG_HELP_ACCESS=       irc\nLANG_HELP_LIST=     \nLANG_ERR_COMMAND= \n"
  },
  {
    "path": "Language/ru-RU/BeatmapSuggest.DefaultLanguage.lang",
    "content": "[ru-RU]\nLANG_GET_BEATMAP_FAILED=    {0}  ,: {1}\nLANG_SUGGEST_MEG={0} ,     [{1} {2}] || [{3} dl] || [{4} mirror]  \"?dl\"/\"?dl all\"\nLANG_NOT_FOUND_ERR=      ID -  beatmapSetID\nLANG_UNKNOWN_TITLE=<unk title>\nLANG_GET_BEATMAP_TIME_OUT=     {0} ,TaskStatus{1}\nLANG_INVAILD_ID=  id {0}\nLANG_UNKOWN_PARAM=  {0}\nLANG_ERROR_INFO_IMCOMPLETE= \nLANG_NO_API_KEY_NOFITY= ApiKey   config.ini, ,   osu!api_key  .  ApiKey:https://osu.ppy.sh/p/api\nLANG_START_DOWNLOAD=  : {0}\nLANG_FINISH_DOWNLOAD=  {0}\nLANG_FAILED_DOWNLOAD= {0}  ,: {1}\nLANG_DOWNLOAD_TASK_COUNT=  {0} .\n"
  },
  {
    "path": "Language/ru-RU/ConfigGUI.DefaultLanguage.lang",
    "content": "[ru-RU]\nBUTTON_OPEN=\nBUTTON_BROWSE=\nBUTTON_FONT=\nBUTTON_COLOR=\nWINDOW_TITLE=\nWINDOW_TITLE_REQUIRE_RESTART=     \nBUTTON_SAVE=\nLABEL_SAVED=!\nLABEL_SAVED_SAVING=...\nCOMMAND_LINE_HINT=[ConfigGUI] \"config\"    .\nTRAY_HIDE_SHOW=/\nTRAY_CONFIG=\nTRAY_OPEN_SYNC_FOLDER= Sync\nTRAY_EXIT=\n"
  },
  {
    "path": "Language/ru-RU/DefaultGUI.Language.lang",
    "content": "[ru-RU]\nUI_DISPLAY= UI\nUI_TIPS_BOTIRC=BotIRC\nUI_TIPS_BOTIRC_PASS=  IRC\nUI_TIPS_STATUS=\nUI_TIPS_DANMAKU=LiveID\nUI_TIPS_OSU_IRC=osu!Chat\nUI_TIPS_TARGET_IRC=ToIRC\nUI_BOTTON_START=\nUI_BOTTON_STOP=\nUI_BOTTON_LOGIN_DANMAKU= \nUI_BOTTON_SWITCH_CON=  CMD UI\nUI_BOTTON_EXIT=\nUI_TIPS_LIVE_ID=LiveID\nUI_TIPS_LIVE_SOURCE=\nUI_INFO_RESTART_REQ=      Sync.  ?\n"
  },
  {
    "path": "Language/ru-RU/DefaultPlugin.Language.lang",
    "content": "[ru-RU]\nLANG_COMMANDS_LOGIN=login <> []         \nLANG_COMMANDS_EXIT=   \nLANG_COMMANDS_CLEAR= \nLANG_COMMANDS_STATUS=  \nLANG_COMMANDS_STOP=  \nLANG_COMMANDS_START= Sync\nLANG_COMMANDS_HELP= \nLANG_COMMANDS_SOURCEMSG=sourcemsg <message>     \nLANG_COMMANDS_CLIENTMSG=clientmsg <message>     \nLANG_COMMANDS_SOURCES=     \nLANG_COMMANDS_MSGMGR=   --help,   \nLANG_COMMANDS_FILTERS=   \nLANG_COMMANDS_DISABLE=disabple < >    OnDisable()\nLANG_COMMANDS_SWITCH_CLIENT=switchclient []     \nLANG_COMMANDS_SOURCELOGIN=sourcelogin [] []   \nLANG_COMMANDS_RESTART= \nLANG_COMMANDS_LANG=lang [cultureName] / \nLANG_COMMANDS_LISTLANG=listlang [--all]  (/) \nLANG_COMMANDS_BILIBILI=setbili <roomID>   Bilibili RoomID\nLANG_COMMANDS_FILTERS_ITEM=\nLANG_COMMANDS_FILTERS_OBJ=\nLANG_COMMANDS_SET_OSU_BOT=setosubot (botirc) (botirc_pw) (targetirc)   OSUIRCBot\nLANG_COMMANDS_BOTIRC_CURRENT= BotIRC: {0:S}\nLANG_COMMANDS_BOTIRC_SET=  BotIRC  {0:S} \nLANG_COMMANDS_IRC_CURRENT= Target IRC: {0:S}\nLANG_COMMANDS_IRC_SET=  Target IRC  {0:S}\nLANG_COMMANDS_TARGET_CURRENT= LiveID: {0:S}\nLANG_COMMANDS_TARGET_SET=  LiveID  {0:S}\nLANG_COMMANDS_CLIENT_NAME=\nLANG_COMMANDS_CLIENT_AUTHOR=\nLANG_COMMANDS_SOURCES_NAME=\nLANG_COMMANDS_SOURCES_AUTHOR=\nLANG_COMMANDS_CURRENT=  {0:S}\nLANG_COMMANDS_DANMAKU_NOT_SUPPORT=   Sendable\nLANG_COMMANDS_EXIT_DONE=  , ,  .\nLANG_COMMANDS_CHAT_IRC_NOTCONNECT=osu!irc  ,    .\nLANG_COMMANDS_DANMAKU_REQUIRE_LOGIN=   !\nLANG_COMMANDS_START_ALREADY_RUN=  \nLANG_COMMANDS_ARGUMENT_WRONG= .\nLANG_COMMANDS_MSGMGR_HELP=\\n--status \\n--limit <> :    - \\n--option <> :auto/force_all/force_limit\nLANG_COMMANDS_MSGMGR_LIMIT=...\nLANG_COMMANDS_MSGMGR_FREE=\nLANG_COMMANDS_MSGMGR_STATUS= MessageManager:{4:S},:{0:D},queueCount/limitCount/recoverTime:{1}/{2}/{3}\nLANG_COMMANDS_MSGMGR_LIMIT_SPEED_SET=    {0}\nLANG_COMMANDS_MSGMGR_LIMIT_STYPE_SET=    {0}\nLANG_SEND_COOKIE_SAVED=   \nLANG_SEND_DONE= .\nLANG_DOUYU_FAIL=    !   {0},  {1}\nLANG_DOUYU_AUTH_SUCC=  Douyutv \nLANG_DOUYU_DANMAKU= : {0}: {1}\nLANG_DOUYU_GIFT=\nLANG_BILIBILI_ONLINECHANGE= : {0}\nLANG_COMMANDS_CLIENTUSERMSG=chatuser <username> <message>  IRC      \nLANG_OSUIRC_LOGIN_SUCCESS= .\nLANG_OSUIRC_LOGIN_FAILED=   osu!irc  , : {0}\nLANG_OSUIRC_NETWORK_INTERRUPTED= ,   .\nLANG_OSUIRC_RECIVER_FINISHED=Reciver thread is finished.\n"
  },
  {
    "path": "Language/ru-RU/NowPlaying.Languages.lang",
    "content": "[ru-RU]\nOSU_PATH_NOT_SET=  osu! ,      osu!.\nFIND_OSU_PATH=     osu! {0:S}\nOSU_PATH_FAIL=     osu!,   .\nERROR_WHILE_FIND_PATH=     osu! {0:S}\nUNKNOWN_COMMAND=  {0:S}\nSTATUS_PLAYING= \nSTATUS_EDITING=\nSTATUS_OTHER=\nSTATUS_TIP_INFO= {0:S} {1:S}\nSTATUS_TIP_INFO_WRAP= {0:S} {1:S}\nERROR_WHILE_SEARCH_MAP=   {0:S}-{1:S} [{2:S}] {3:S}\nCONSOLE_OUTPUT_RESULT=:{0:S} ({1}ms) HP/CS/AR/OD: {2}{3}{4}{5}\nOUTPUT_RESULT= {0}:{1}\nCURRENT_IDLE=   \n"
  },
  {
    "path": "Language/ru-RU/OsuRTDataProvider.DefaultLanguage.lang",
    "content": "[ru-RU]\nLANG_OSU_NOT_FOUND=[ID:{0}]    osu!.exe\nLANG_OSU_FOUND=[ID:{0}] osu!.exe \nLANG_INIT_PLAY_FINDER_FAILED=[ID:{0}] PlayerFinder  !    {1} \nLANG_INIT_PLAY_FINDER_SUCCESS=[ID:{0}] PlayerFinder !\nLANG_INIT_STATUS_FINDER_FAILED=[ID:{0}] StatusFinder  !    {1} \nLANG_INIT_STATUS_FINDER_SUCCESS=[ID:{0}] StatusFinder !\nLANG_INIT_BEATMAP_FINDER_FAILED=[ID:{0}] BeatmapFinder  !    {1} \nLANG_INIT_BEATMAP_FINDER_SUCCESS=[ID:{0}] BeatmapFinder !\nLANG_INIT_MODE_FINDER_FAILED=[ID:{0}] ModeFinder  !    {1} \nLANG_INIT_MODE_FINDER_SUCCESS=[ID:{0}] ModeFinder !\nLANG_BEATMAP_NOT_FOUND=  \nListenInterval= (ms)\nEnableTourneyMode= \nTeamSize= \nDebugMode= \nForceOsuSongsDirectory=    osu!\nLANG_TOURNEY_HINT= : {0}\nCHECK_GOTO_RELEASE_PAGE_HINT= \"ortdp releases\",       \nLANG_CHECK_ORTDP_UPDATE=   OsuRTDataProvider({0})\nGameMode= \nDisableProcessNotFoundInformation=  osu!,   \nEnableModsChangedAtListening= ,   ()\n"
  },
  {
    "path": "Language/ru-RU/RealTimePPDisplayer.DefaultLanguage.lang",
    "content": "[ru-RU]\nUI_MENU_TOPMOST=   \nTEXT_MODE_OUTPUT_PATH_FORMAT=[RealTimePPDisplayer]  pp: {0}\nMMF_MODE_OUTPUT_PATH_FORMAT=[RealTimePPDisplayer]  Memory Mapping: {0}\nFPS=FPS\nTextOutputPath=  \nDisplayHitObject= \nFontName=\nPPFontSize=  pp\nPPFontColor=  pp\nHitCountFontSize=  \nHitCountFontColor=  \nBackgroundColor= \nWindowHeight= \nWindowWidth= \nSmoothTime=Smooth Time\nTopmost=   \nWindowTextShadow=WindowTextShadow\nOutputMethods= \nDebugMode= \nPPFormat= pp\nHitCountFormat= \nRoundDigits=  \nIgnoreTouchScreenDecrease=  \nUseUnicodePerformanceInformation=    \nCHECK_RTPPD_UPDATE=[RealTimePPDisplayer]   RealTimePPDisplayer({0})\nCHECK_OPPAI_UPDATE=[RealTimePPDisplayer]   oppai-ng ({0})\nCHECK_GOTO_RELEASE_PAGE_HINT=[RealTimePPDisplayer] \"rtpp releases\",       .\nUI_UPDATE_CONFIGGUI_MESSAGEBOX=,  ConfigGUI   0.2.1  .\nHINT_BEATMAP_NO_FOUND=[RealTimePPDisplayer]  !     Songs. ( OsuRTDataProvider,     ).\nHINT_CANNOT_WATCH_OTHER_PLAYER=[RealTimePPDisplayer]    . (     ,   \"By CuteSync Proxy\"   api ).\nHINT_POBT_VERSION_LOWER=[RealTimePPDisplayer]  PublicOsuBotTransfer ,  1.3.0      \"By CuteSync Proxy\".\nMBX_POBT_VERSION_NO_INSTALLED= PublicOsuBotTransfer  .\nUI_OPENEDITOR_BUTTON_CONTENT= \nUI_MULTIOUTPUTEDITOR_BUTTON_CONTENT= \nUI_NAME_LABEL=:\nUI_FORMAT_LABEL=: \nUI_DELETETHIS_BUTTON_CONTENT= \nUI_SMOOTH_CHECKBOX_CONTENT=\nUI_EDITFORMAT_BUTTON_CONTENT=\nUI_SMMOTH_CHECKBOX_TOOLTIP= \nUI_TYPE_COMBOBOX_TOOLTIP=  \nUI_FORMATTER_COMBOBOX_TOOLTIP= \nUI_MODES_MULTISELECTCOMBOBOX_TOOLTIP=  \nRankingSendPerformanceToChat= pp    \nByCuteSyncProxy=By CuteSync Proxy\nApiKey=Osu! Api \nFormatter=\n"
  },
  {
    "path": "Language/ru-RU/RecentlyUserQuery.DefaultLanguage.lang",
    "content": "[ru-RU]\nLANG_HELP=\\n     osu!irc ( osu!irc , , \"?\"   )\\nrecently --status |     (  osu!irc)\\nrecently --u <userName> |   <userName> (     osu!irc)\\nrecently --i <userId> |   <userId> (     osu!irc)\\nrecently |     ID, ID       ,  \"?ban --i\" (  osu!irc)\\nrecently --disable |    ,    (  osu!irc)\\nrecently --start |    (  osu!irc)\\nrecently --realloc <count> |   ,     (  osu!irc)\\nrecently --recently |  id  \\n\nLANG_MSG_STATUS= MessageRecord: {0} | recordCount/ : {1}/{2}\nLANG_RUNNING=\nLANG_STOP=\nLANG_MSG_DISABLE=      \nLANG_MSG_START=   \nLANG_MSG_REALLOC_ERR=MessageRecord:  \nLANG_MSG_REALLOC=        {0}\nLANG_MSG_NOTIMPLENT= \nLANG_MSG_UNKNOWNCOMMAND= ."
  },
  {
    "path": "Language/ru-RU/Sync.Tools.DefaultI18n.lang",
    "content": "[ru-RU]\nLANG_Loading=...\nLANG_Plugins= {0:D} (-)\nLANG_Sources= {0:D} (-)\nLANG_Client= {0:D} (-)\nLANG_Error=CAN NOT INITIAL WARPPERS!   .\nLANG_Commands= {0:D} (-)\nLANG_Filters= {0:D} (-)\nLANG_Ready=\nLANG_RqueireLogin=,    \nLANG_AccountName= :\nLANG_AccountPw=:\nLANG_AccountSave=  !\nLANG_Start=...\nLANG_Stopping=...\nLANG_Restarting=...\nLANG_LoadingPlugin= {0:S} ...\nLANG_LoadPluginErr=  {0:S} ({1:S})\nLANG_NotPluginErr={0:S}   ({1:S})\nLANG_NotConfig=,  'config.ini'  .\nLANG_NoSource=    wrapper. ,  .\nLANG_MissSource=   -  .\nLANG_SetSource= {0:S}   live-\nLANG_SupportSend=        (: login [] [])\nLANG_CertLength= : {0:D}\nLANG_CertExist=  ,      \nLANG_SendNotReady=      danmaku.\nLANG_UnknowCommand= ,  'help'   .\nLANG_CommandFail=   ,  'help'   .\nLANG_ConfigFile=\nLANG_UserCount=   : {0:D}\nLANG_UserCount_Change= {0:S}  {1:D}\nLANG_UserCount_Change_Increase=\nLANG_UserCount_Change_Decrease=\nLANG_Source_Disconnecting=   ...\nLANG_Source_Disconnected=   .    3 .\nLANG_Source_Disconnected_Succ=    .\nLANG_Source_Connect=   ...\nLANG_Source_Connected_Succ=    !\nLANG_Current_Online=  : {0:D}\nLANG_Gift_Sent=  {0:D} {1:S}\nLANG_Config=: \nLANG_Config_Status_OK=OK, LiveID:{0}\nLANG_Config_Status_Fail=  .\nLANG_Source={0:S}: \nLANG_IRC=: \nLANG_Danmaku=Danmaku : \nLANG_Status_Connected=\nLANG_Status_NotConenct=\nLANG_Loading_Config= ...\\n\nLANG_Welcome=osu!Live Sync  {0:S}\nLANG_Help=  'help'   \nLANG_Command=\nLANG_Command_Description=\nLANG_Plugin_Cycle_Reference=   . {0:S}      .\nLANG_IRC_Connecting=[] ...\nLANG_IRC_Disconnect=   IRC...\nLANG_IRC_Connect_Timeout=   ,    .\nLANG_IRC_Ready=[] !\nLANG_MsgMgr_Limit=  ,  irc    danmaku   ?send\nLANG_MsgMgr_Free=   .\nLANG_Instance_Exist=Sync  .\nLANG_COMMANDS_LOGIN=login <> []        \nLANG_COMMANDS_EXIT= Sync  \nLANG_COMMANDS_CLEAR= \nLANG_COMMANDS_STATUS=    \nLANG_COMMANDS_STOP= Sync\nLANG_COMMANDS_START= Sync\nLANG_COMMANDS_HELP= \nLANG_COMMANDS_SOURCEMSG=sourcemsg <message>     \nLANG_COMMANDS_CLIENTMSG=clientmsg <message>     \nLANG_COMMANDS_SOURCES=     \nLANG_COMMANDS_MSGMGR=   --help,   \nLANG_COMMANDS_FILTERS=   \nLANG_COMMANDS_DISABLE=disable < >    OnDisable()\nLANG_COMMANDS_SWITCH_CLIENT=switchclient []    \nLANG_COMMANDS_SOURCELOGIN=sourcelogin [] []   \nLANG_COMMANDS_RESTART= \nLANG_COMMANDS_LANG=lang [cultureName] / \nLANG_COMMANDS_LISTLANG=listlang [--all]  (/) \nLANG_COMMANDS_FILTERS_ITEM=\nLANG_COMMANDS_FILTERS_OBJ=\nLANG_COMMANDS_CLIENT_NAME=\nLANG_COMMANDS_CLIENT_AUTHOR=\nLANG_COMMANDS_SOURCES_NAME=\nLANG_COMMANDS_SOURCES_AUTHOR=\nLANG_COMMANDS_CURRENT=  {0:S}\nLANG_COMMANDS_DANMAKU_NOT_SUPPORT=   Sendable\nLANG_COMMANDS_CHAT_IRC_NOTCONNECT=osu!irc  ,    .\nLANG_COMMANDS_DANMAKU_REQUIRE_LOGIN=   !\nLANG_COMMANDS_START_ALREADY_RUN=  \nLANG_COMMANDS_ARGUMENT_WRONG= .\nLANG_COMMANDS_MSGMGR_HELP=\\n--status \\n--limit <> :    - \\n--option <> :auto/force_all/force_limit\nLANG_COMMANDS_MSGMGR_LIMIT=MessageManager ...\nLANG_COMMANDS_MSGMGR_FREE=MessageManager ...\nLANG_COMMANDS_MSGMGR_STATUS=MessageManager mode:{4:S},status:{0:D},queueCount/limitCount/recoverTime:{1}/{2}/{3}\nLANG_COMMANDS_MSGMGR_LIMIT_SPEED_SET=    {0}\nLANG_COMMANDS_MSGMGR_LIMIT_STYPE_SET=    {0}\nLANG_COMMANDS_START_NO_SOURCE=    .\nLANG_COMMANDS_START_NO_CLIENT=    .\nLANG_COMMANDS_CURRENT_LANG= : {0:S}\t{1:S}\nLANG_COMMANDS_LANG_SWITCHED=    {1:S}({0:S})\nLANG_COMMANDS_LANG_NOT_FOUND=  !  'listlang'     \nLANG_UPDATE_DONE= .  ,   .\nLANG_INSTALL_DONE= .  ,   .\nLANG_PLUGIN_NOT_FOUND= {0}  \nLANG_REMOVE_DONE= .    .\nLANG_VERSION_LATEST={0}  \nLANG_UPDATE_CHECK_ERROR=    [{0}] :  {1} : {2}\nLANG_UPDATE_ERROR=  :  {0} : {1}\nLANG_SOURCE_NOT_SUPPORT_SEND= {0}     .\nLANG_NO_PLUGIN_SELECT=   \nLANG_PLUGIN_DISABLED=\nLANG_COMMANDS_CLIENTUSERMSG=chatuser <username> <message>        IRC\nLANG_COMMANDS_EXIT_DONE= , ,  .\nLANG_NO_ANY_SOURCE=\" ,      'plugins install DefaultPlugin',       \".\n"
  },
  {
    "path": "Language/zh-CN/BanManagerPlugin.DefaultLanguage.lang",
    "content": "[zh-CN]\nLANG_HELP_BAN=ֹĳuser/id/regexϢirc\nLANG_HELP_UNBAN=ֹĳuser/id/regex\nLANG_HELP_WHITELIST=ĳuser/id/regex˽һֱȨ޷Ϣirc\nLANG_HELP_REMOVE_WHITELIST=ĳuser/id/regexӰƳ\nLANG_HELP_ACCESS=÷ϢircȨ\nLANG_HELP_LIST=ȡֹ߽û͹\nLANG_ERR_COMMAND=ָ\n"
  },
  {
    "path": "Language/zh-CN/DefaultGUI.Language.lang",
    "content": "[zh-CN]\nUI_DISPLAY=ʾUI\nUI_TIPS_BOTIRC=BotIRC\nUI_TIPS_BOTIRC_PASS=IRC\nUI_TIPS_STATUS=״̬\nUI_TIPS_DANMAKU=ֱĻ\nUI_TIPS_OSU_IRC=osu! \nUI_TIPS_TARGET_IRC=ĿIRC\nUI_BOTTON_START=ʼ\nUI_BOTTON_STOP=ֹͣ\nUI_BOTTON_LOGIN_DANMAKU=¼ֱ\nUI_BOTTON_SWITCH_CON=л̨\nUI_BOTTON_EXIT=˳\nUI_TIPS_LIVE_ID=ֱ\nUI_TIPS_LIVE_SOURCE=ֱԴ\nUI_INFO_RESTART_REQ=ıֱԴ֮Ч\n"
  },
  {
    "path": "Language/zh-CN/DefaultPlugin.Language.lang",
    "content": "[zh-CN]\nLANG_COMMANDS_LOGIN=login <user> [pass] ¼Ŀ굯ĻվĻ͹\nLANG_COMMANDS_EXIT=˳\nLANG_COMMANDS_CLEAR=Ļ\nLANG_COMMANDS_STATUS=õǰ״̬\nLANG_COMMANDS_STOP=ֹͣǰ\nLANG_COMMANDS_START=ʼͬ\nLANG_COMMANDS_HELP=ӡϢ\nLANG_COMMANDS_SOURCEMSG=danmaku <message> ͵Ļ\nLANG_COMMANDS_CLIENTMSG=chat <message> IRCϢ\nLANG_COMMANDS_SOURCES=õǰеĻԴб\nLANG_COMMANDS_MSGMGR=鿴Ϣ,--helpȡ\nLANG_COMMANDS_FILTERS=беǰϢ\nLANG_COMMANDS_DISABLE=ͽϢ disable ()\nLANG_COMMANDS_SWITCH_CLIENT=лָClientʵΪȡClientб\nLANG_COMMANDS_SOURCELOGIN=¼ĻԴ sourcelogin [û] []\nLANG_COMMANDS_RESTART=Ӧó\nLANG_COMMANDS_LANG=lang [cultureName] Get/Set language\nLANG_COMMANDS_LISTLANG=listlang [--all] List (supported/all) languages\nLANG_COMMANDS_BILIBILI=setbili roomID BilibiliֱԴķ\nLANG_COMMANDS_FILTERS_ITEM=\nLANG_COMMANDS_FILTERS_OBJ=\nLANG_COMMANDS_SET_OSU_BOT=osuĻ˺ setosubot (˺) (IRC) (ĿIRC)\nLANG_COMMANDS_BOTIRC_CURRENT=ǰBotIRC: {0:S}\nLANG_COMMANDS_BOTIRC_SET=ǰBotIRCΪ {0:S}\nLANG_COMMANDS_IRC_CURRENT=ǰĿIRC: {0:S}\nLANG_COMMANDS_IRC_SET=ǰĿIRCΪ \nLANG_COMMANDS_TARGET_CURRENT=ǰֱID: {0:S}\nLANG_COMMANDS_TARGET_SET=ǰֱIDΪ \nLANG_COMMANDS_CLIENT_NAME=Client\nLANG_COMMANDS_CLIENT_AUTHOR=\nLANG_COMMANDS_SOURCES_NAME=ĻԴ\nLANG_COMMANDS_SOURCES_AUTHOR=\nLANG_COMMANDS_CURRENT=ǰΪ {0:S}\nLANG_COMMANDS_DANMAKU_NOT_SUPPORT=ʾǰĻԴַ֧͵ĻĻԴ\\n\nLANG_COMMANDS_EXIT_DONE=˳ɣڻδرգǿƹرա\nLANG_COMMANDS_CHAT_IRC_NOTCONNECT=osu! irc δӣܷϢ\nLANG_COMMANDS_DANMAKU_REQUIRE_LOGIN=¼ܷ͵Ļ!\nLANG_COMMANDS_START_ALREADY_RUN=ͬʵѾС\nLANG_COMMANDS_ARGUMENT_WRONG=ȷ\nLANG_COMMANDS_MSGMGR_HELP=\\n--status :鿴ǰϢϢ\\n--limit <ֵ> :ƷϢĵȼԽ;Խ״ܿ\\n--option <> :ùܿصķʽautoԶܿأforce_allǿȫ,force_limitǽʹ?sendϢ\nLANG_COMMANDS_MSGMGR_LIMIT=...\nLANG_COMMANDS_MSGMGR_FREE=\nLANG_COMMANDS_MSGMGR_STATUS=MessageManager mode:{4:S},status:{0:D},queueCount/limitCount/recoverTime:{1}/{2}/{3}\nLANG_COMMANDS_MSGMGR_LIMIT_SPEED_SET=ƷٶȵȼΪ{0}\nLANG_COMMANDS_MSGMGR_LIMIT_STYPE_SET=ϢĹƷʽΪ{0}\nLANG_SEND_COOKIE_SAVED=½Ϣɹ\nLANG_SEND_DONE=\nLANG_DOUYU_FAIL=״̬ʧ! ڴ{0}, {1}\nLANG_DOUYU_AUTH_SUCC=֤ɹ\nLANG_DOUYU_DANMAKU=յĻ: {0}:{1}\nLANG_DOUYU_GIFT=\nLANG_BILIBILI_ONLINECHANGE=ֱ仯 {0}\n"
  },
  {
    "path": "Language/zh-CN/NowPlaying.Languages.lang",
    "content": "[zh-CN]\nOSU_PATH_NOT_SET=δosuļ·ѰǰеosuԶ\nFIND_OSU_PATH=ҵosu·! {0:S}\nOSU_PATH_FAIL=δosuļ·Ҳûеosu޷ʹô˲߼ܣú·osuSyncܼʹ\nERROR_WHILE_FIND_PATH=ϸϢʧܣ {0:S}\nUNKNOWN_COMMAND=Ч{0:S}\nSTATUS_PLAYING=\nSTATUS_EDITING=\nSTATUS_OTHER=\nSTATUS_TIP_INFO={0:S}{1:S}\nSTATUS_TIP_INFO_WRAP={0:S}{1:S}\nERROR_WHILE_SEARCH_MAP=Լ{0:S}-{1:S} [{2:S}]ʱʧܣ{3:S}\nCONSOLE_OUTPUT_RESULT=ļ{0:S} (ʱ: {1}) HP/CS/AR/OD: {2}{3}{4}{5}\nOUTPUT_RESULT=ǰ{0}:{1}\nCURRENT_IDLE=ǰûڴͼ\n"
  },
  {
    "path": "Language/zh-CN/RecentlyUserQuery.DefaultLanguage.lang",
    "content": "[zh-CN]\nLANG_HELP=\\nָcmd˺osu!irc˹ͨ(osu!ircڿͷ\"?\")\\nrecently --status |ȡǰϢ¼״̬Ϣ(osu!irc)\\nrecently --u <userName> |ȡû<userName>ʷϢ(osu!irc)\\nrecently --i <userId> |ȡû<userId>ʷϢ(osu!irc)\\nrecently |ȡûֺid,idִ\"?ban --i\"ָ(osu!irc)\\nrecently --disable |رռ¼йܲ(osu!irc)\\nrecently --start |¿ʼ¼(osu!irc)\\nrecently --realloc <count> |·¼¼(osu!irc)\\nrecently --recently |ȡûid\\n\nLANG_MSG_STATUS=MessageRecord status: {0} | recordCount/Capacity : {1}/{2}\nLANG_RUNNING=running\nLANG_STOP=stopped\nLANG_MSG_DISABLE=Ϣ¼ѽã\nLANG_MSG_START=Ϣ¼\nLANG_MSG_REALLOC_ERR=MessageRecord: ָ\nLANG_MSG_REALLOC=Ϣ¼ڿɼ¼ {0} ʷ¼\nLANG_MSG_NOTIMPLENT=~\nLANG_MSG_UNKNOWNCOMMAND=δ֪\n"
  },
  {
    "path": "Language/zh-CN/Sync.Tools.DefaultI18n.lang",
    "content": "[zh-CN]\nLANG_Loading=ȡ....\nLANG_Plugins= {0:D}  \nLANG_Sources= {0:D}  ֱԴ\nLANG_Client= {0:D}  Client\nLANG_Error=ܳʼȷǷѾװֱԴ.\nLANG_Commands= {0:D}  \nLANG_Filters= {0:D}  \nLANG_Ready=׼\nLANG_RqueireLogin=¼RnW˺\nLANG_AccountName=û:\nLANG_AccountPw=:\nLANG_AccountSave=˻ɹ! ʼӵ\nLANG_Start=ʼ....\nLANG_Stopping=ֹͣ...\nLANG_Restarting=¿ʼ...\nLANG_LoadingPlugin= {0:S} ...\nLANG_LoadPluginErr= {0:S} ({1:S})\nLANG_NotPluginErr={0:S} osuSync ({1:S})\nLANG_NotConfig= 'config.ini' ٿʼͬ\nLANG_NoSource=޷ҵκֱԴ밲װһֱԴ\nLANG_MissSource=ҲĬƥֱԴֱʹõһ\nLANG_SetSource= {0:S} ΪֱĻԴ\nLANG_SupportSend=ʾ:ǰĻԴ֧Ϸڷ͵ĻԴĹܣlogin [û] [] ¼!(û߿ѡ)\nLANG_CertLength=Certification: {0:D}\nLANG_CertExist=ʾǰе¼Certification¼踲ǣlogin [û] []иǣûѡ룩\nLANG_SendNotReady=ǰClientδ־ĻͿã볢ʹLogin¼\nLANG_UnknowCommand=δ֪ help鿴б\nLANG_CommandFail=ִʧܣ help鿴б\nLANG_ConfigFile=ļ\nLANG_UserCount=û: {0:D}\nLANG_UserCount_Change=ֱΧ{0:S}{1:D}\nLANG_UserCount_Change_Increase=\nLANG_UserCount_Change_Decrease=\nLANG_Source_Disconnecting=ڶϿĻԴ....\nLANG_Source_Disconnected=ӱϿ3\nLANG_Source_Disconnected_Succ=ԴϿӳɹ\nLANG_Source_Connect=ӵĻԴ....\nLANG_Source_Connected_Succ=Դӳɹ\nLANG_Current_Online=ǰ: {0:D}\nLANG_Gift_Sent=͸{O:D}{1:S}!\nLANG_Config=ļ: \nLANG_Config_Status_OK=OK, ID:{0}\nLANG_Config_Status_Fail=δóɹ\nLANG_Source=Դ{0:S}: \nLANG_IRC=Client:\nLANG_Danmaku=Ļ:\nLANG_Status_Connected=\nLANG_Status_NotConenct=δ\nLANG_Loading_Config=ڶȡļ....\\n\nLANG_Welcome=ӭʹ osuֱĻͬ ver {0:S} \nLANG_Help= 'help' ðб\\n\\n\nLANG_Command=\nLANG_Command_Description=\nLANG_MsgMgr_Limit=ǰϢ ʼ ƣֻ?sendݲŻᷢ͵ircƵ\nLANG_MsgMgr_Free=ǰϢ  ,ݿֱӷ͵ircƵ\nLANG_Plugin_Cycle_Reference=ֲ֮ѭùϵ {0:S} ᰴտָϵм\n"
  },
  {
    "path": "README.md",
    "content": "# Sync\n\n![SyncIcon](Sync/Resources/SyncIcon.ico)\n\n## [Download Releases](https://github.com/OsuSync/Sync/releases) , [UserGuide](https://github.com/Deliay/Sync/wiki/UserReadMe_en) , [Official Plugins](https://github.com/Deliay/SyncPlugin)\n[QQ群](https://jq.qq.com/?_wv=1027&k=5y2CVZ6) / [Discord](https://discord.gg/KG86sWS)\n----------------------------------\nSync your live chat to osu!~\n\n[MIT License](LICENSE)\n"
  },
  {
    "path": "Sync/Client/ClientEvent.cs",
    "content": "﻿using Sync.MessageFilter;\nusing Sync.Plugins;\nusing System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace Sync.Client\n{\n    /// <summary>\n    /// Singleton Client event dispathcher\n    /// </summary>\n    public class ClientEvents : BaseEventDispatcher<IClientEvent>\n    {\n        public readonly static ClientEvents Instance = new ClientEvents();\n        private ClientEvents()\n        {\n            EventDispatcher.Instance.RegisterNewDispatcher(GetType());\n        }\n    }\n\n    /// <summary>\n    /// Base client event interface flag\n    /// </summary>\n    public interface IClientEvent : IBaseEvent { }\n\n    /// <summary>\n    /// Fire when client start work (fire time decide by client)\n    /// </summary>\n    public struct ClientStartWorkEvent : IClientEvent\n    {\n        public ClientWorkWrapper Client { get => SyncHost.Instance.ClientWrapper; }\n    }\n\n    /// <summary>\n    /// Fire when client stop work\n    /// </summary>\n    public struct ClientStopWorkEvent : IClientEvent\n    {\n\n    }\n\n    /// <summary>\n    /// Fire when Client recive a IRCMessage\n    /// </summary>\n    public struct ClientOnMessageEvent : IClientEvent\n    {\n        public IRCMessage Message { get; }\n\n        public ClientOnMessageEvent(IRCMessage message)\n        {\n            Message = message;\n        }\n    }\n}\n"
  },
  {
    "path": "Sync/Client/ClientManager.cs",
    "content": "﻿using Sync.MessageFilter;\nusing System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace Sync.Client\n{\n    /// <summary>\n    /// The manager for Clients\n    /// </summary>\n    public class ClientManager\n    {\n        private LinkedList<DefaultClient> clients;\n        public IReadOnlyList<DefaultClient> Clients { get => clients.ToList(); }\n        public int Count { get => clients.Count; }\n\n        public static readonly ClientManager Instance = new ClientManager();\n        private ClientManager()\n        {\n            clients = new LinkedList<DefaultClient>();\n        }\n\n        public void AddAllClient(params DefaultClient[] clients)\n        {\n            foreach (var client in clients)\n            {\n                if (!this.clients.Contains(client))\n                {\n                    this.clients.AddLast(client);\n                }\n            }\n        }\n\n        public bool AddClient(DefaultClient client)\n        {\n            if(clients.Contains(client))\n            {\n                return false;\n            }\n            else\n            {\n                clients.AddLast(client);\n                return true;\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "Sync/Client/ClientWorkWrapper.cs",
    "content": "﻿using Sync.Tools;\r\nusing System;\r\nusing System.Collections.Generic;\r\nusing System.Linq;\r\nusing System.Text;\r\nusing System.Threading.Tasks;\r\n\r\nnamespace Sync.Client\r\n{\r\n    /// <summary>\r\n    /// A wrapper for Client instance switch and dispatch\r\n    /// </summary>\r\n    public class ClientWorkWrapper\r\n    {\r\n        public DefaultClient Client { get; internal set; }\r\n        \r\n        private ClientManager clients;\r\n\r\n        public ClientWorkWrapper(ClientManager manager)\r\n        {\r\n            clients = manager;\r\n            ResetClient();\r\n        }\r\n\r\n        /// <summary>\r\n        /// Call when reload client form confiuration file\r\n        /// </summary>\r\n        public void ResetClient()\r\n        {\r\n            DefaultClient client = clients.Clients.FirstOrDefault(p => p.ClientName == DefaultConfiguration.Instance.Client);\r\n\r\n            if (Client != null && Client != client)\r\n            {\r\n                Client.SwitchOtherClient();\r\n            }\r\n            \r\n            if (client == null)\r\n            {\r\n                if (clients.Count == 0) return;\r\n                client = clients.Clients.First();\r\n            }\r\n\r\n\r\n            Client = client;\r\n\r\n            Client.SwitchThisClient();\r\n        }\r\n    }\r\n}\r\n"
  },
  {
    "path": "Sync/Client/DefaultReciveClient.cs",
    "content": "﻿using Sync.MessageFilter;\nusing Sync.Plugins;\nusing Sync.Source;\nusing System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace Sync.Client\n{\n    public abstract class DefaultClient\n    {\n\n        //Author and Client Name\n        public string Author { get; }\n        public string ClientName { get; }\n\n        public BaseEventDispatcher<IClientEvent> EventBus { get => ClientEvents.Instance; }\n\n        public string NickName { get; protected set; }\n\n        /// <summary>\n        /// Invoke while user switch to other client instance\n        /// </summary>\n        public abstract void SwitchOtherClient();\n        /// <summary>\n        /// Invoke while user switch to this instance\n        /// </summary>\n        public abstract void SwitchThisClient();\n\n        public DefaultClient(string Author, string Name)\n        {\n            this.Author = Author;\n            this.ClientName = Name;\n        }\n\n\n        /// <summary>\n        /// Raise event and pass messages\n        /// </summary>\n        /// <param name=\"msg\">Bypass message</param>\n        protected void EnqueueMessage(IRCMessage msg)\n        {\n            EventBus.RaiseEvent(new ClientOnMessageEvent(msg));\n        }\n\n        public abstract void StartWork();\n        public abstract void StopWork();\n        public abstract void Restart();\n\n        public SourceStatus CurrentStatus { get; protected set; }\n\n        /// <summary>\n        /// SendMessage to Client\n        /// WARNING: SEND MESSAGE DIRECTLY WHIT THIS WILL NOT PASS BY FILTERS!\n        /// </summary>\n        /// <param name=\"message\"></param>\n        public abstract void SendMessage(IMessageBase message);\n\n    }\n}\n"
  },
  {
    "path": "Sync/Command/CommandDispatch.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\n\nnamespace Sync.Command\n{\n    /// <summary>\n    /// Delegate for one Command\n    /// </summary>\n    /// <param name=\"arg\">Command args</param>\n    /// <returns></returns>\n    public delegate bool CommandDelegate(Arguments arg);\n\n    /// <summary>\n    /// A typedef for arguments list\n    /// </summary>\n    public class Arguments : List<string>\n    {\n        public Arguments()\n        {\n\n        }\n\n        public Arguments(params string[] args)\n        {\n            AddRange(args);\n        }\n\n        public static implicit operator Arguments(string[] args)\n        {\n            return new Arguments(args);\n        }\n    }\n\n    /// <summary>\n    /// A Hashmap command dispatcher\n    /// </summary>\n    public class CommandDispatch\n    {\n        private Dictionary<string, CommandDelegate> cmdList = new Dictionary<string, CommandDelegate>();\n        private Dictionary<string, string> cmdDest = new Dictionary<string, string>();\n        public int count\n        {\n            get { return cmdList.Count; }\n        }\n\n        /// <summary>\n        /// Bind command to string\n        /// </summary>\n        /// <param name=\"name\">Invoke name</param>\n        /// <param name=\"func\">Functor</param>\n        /// <param name=\"desc\">Description</param>\n        /// <returns><see cref=\"true\"/> success, <see cref=\"false\"/> fail</returns>\n        public bool bind(string name, CommandDelegate func, string desc)\n        {\n            if (cmdList.ContainsKey(name)) return false;\n            cmdList.Add(name, func);\n            cmdDest.Add(name, desc);\n            return true;\n        }\n\n        public CommandDelegate get(string name)\n        {\n            if (cmdList.ContainsKey(name)) return cmdList[name];\n            else return null;\n        }\n\n        public IDictionary<string, string> getCommandsHelp()\n        {\n            return cmdDest;\n        }\n\n        public bool invoke(string name, Arguments args)\n        {\n            try\n            {\n                if (cmdList.ContainsKey(name)) return cmdList[name](args);\n                else return false;\n            }\n            catch(Exception e)\n            {\n                Tools.IO.CurrentIO.Write(e.Message);\n                Tools.IO.CurrentIO.Write(e.StackTrace);\n                Tools.IO.CurrentIO.Write(e.Source);\n                return false;\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "Sync/Command/CommandManager.cs",
    "content": "﻿using Sync.Tools;\nusing static Sync.Tools.DefaultI18n;\nusing System;\n\nnamespace Sync.Command\n{\n    /// <summary>\n    /// Command manager for plugins commands\n    /// </summary>\n    public class CommandManager\n    {\n        CommandDispatch dispatch;\n\n        public CommandManager()\n        {\n            dispatch = new CommandDispatch();\n        }\n\n        /// <summary>\n        /// Register command via this dispatch\n        /// </summary>\n        public CommandDispatch Dispatch\n        {\n            get { return dispatch; }\n        }\n\n        /// <summary>\n        /// Invoke command in string\n        /// </summary>\n        /// <param name=\"cmd\">Command</param>\n        public void invokeCmdString(string cmd)\n        {\n            if (cmd == null || cmd.Length == 0) return;\n            string[] args = cmd.Split(\" \".ToCharArray(), 2);\n\n            if(args.Length < 1 )\n            {\n                IO.CurrentIO.Write(LANG_UnknowCommand);\n                return;\n            }\n            string arg = string.Empty;\n            if (args.Length > 1) arg = args[1];\n            \n            if (!dispatch.invoke(args[0], (arg == string.Empty ? new Arguments() : arg.Split(' '))))\n            {\n                IO.CurrentIO.Write(LANG_CommandFail);\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "Sync/Dependence/config.ini",
    "content": "[config]\nLiveRoomID=0\nTargetIRC=noname\nBotIRC=noname\nBotIRCPassword=password\nProvider=BiliBili"
  },
  {
    "path": "Sync/Event/EventDispatcher.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading;\nusing System.Threading.Tasks;\n\nnamespace Sync.Plugins\n{\n    /// <summary>\n    /// Base event class\n    /// </summary>\n    public interface IBaseEvent\n    {\n    }\n\n    public class EventDispatcherTaskScheduler : TaskScheduler\n    {\n        private LinkedList<Task> tasks = new LinkedList<Task>();\n        protected override IEnumerable<Task> GetScheduledTasks()\n        {\n            return tasks;\n        }\n\n        protected override void QueueTask(Task task)\n        {\n            var thread = new Thread(() => { TryExecuteTask(task); });\n            thread.Start();\n        }\n\n        protected override bool TryExecuteTaskInline(Task task, bool taskWasPreviouslyQueued)\n        {\n            return TryExecuteTask(task);\n        }\n    }\n\n    public abstract class BaseEventDispatcher<T> where T : IBaseEvent\n    {\n        /// <summary>\n        /// private event for call\n        /// </summary>\n        /// <param name=\"eventType\"></param>\n        /// <param name=\"insance\"></param>\n        private void raiseEventAsync<Event>(Event insance) where Event : T\n        {\n            EventDispatcher.Instance.RaiseEventAsync(this.GetType(), insance);\n        }\n\n        private void raiseEvent<Event>(Event insance) where Event : T\n        {\n            EventDispatcher.Instance.RaiseEvent(this.GetType(), insance);\n        }\n\n        /// <summary>\n        /// public virtual for sub-classes override\n        /// </summary>\n        /// <typeparam name=\"Event\"></typeparam>\n        /// <param name=\"event\"></param>\n        public virtual void RaiseEventAsync<Event>(Event @event) where Event : T\n        {\n            raiseEventAsync(@event);        \n        }\n\n        /// <summary>\n        /// public virtual for sub-classes override\n        /// </summary>\n        /// <typeparam name=\"Event\"></typeparam>\n        /// <param name=\"event\"></param>\n        public virtual void RaiseEvent<Event>(Event @event) where Event : T\n        {\n            raiseEvent(@event);\n        }\n\n        /// <summary>\n        /// regist class for event bind\n        /// </summary>\n        /// <typeparam name=\"Event\"></typeparam>\n        /// <param name=\"handler\"></param>\n        public void BindEvent<Event>(EventHandlerFunc<Event> handler) where Event : T\n        {\n            EventDispatcher.Instance.RegisterEventHandler(GetType(), handler);\n        }\n    }\n\n    /// <summary>\n    /// the generic handler impl\n    /// </summary>\n    /// <typeparam name=\"T\">which typeof Event should handle</typeparam>\n    /// <param name=\"event\">the event fired you target handler</param>\n    /// <returns></returns>\n    public delegate void EventHandlerFunc<Event>(Event @event) where Event : IBaseEvent;\n    \n    public class HandlerList : LinkedList<object>\n    {\n\n    }\n\n    /// <summary>\n    /// typedef for LinkedList\n    /// </summary>\n    public class Dispatcher : Dictionary<Type, HandlerList>\n    {\n        \n    }\n\n    /// <summary>\n    /// [Singleton] Global event dispatcher\n    /// </summary>\n    public class EventDispatcher\n    {\n        private Dictionary<Type, Dispatcher> dispatchers = new Dictionary<Type, Dispatcher>();\n        private TaskScheduler tasks = new EventDispatcherTaskScheduler();\n\n        private EventDispatcher()\n        {\n\n        }\n\n        public static readonly EventDispatcher Instance = new EventDispatcher();\n\n        /// <summary>\n        /// Register new event type[event dispatcher]\n        /// </summary>\n        /// <typeparam name=\"EventDispatcher\">New dispatcher classes</typeparam>\n        /// <returns></returns>\n        public void RegisterNewDispatcher<EventDispatcher, TEvent>() where EventDispatcher : BaseEventDispatcher<TEvent> where TEvent : IBaseEvent\n        {\n            RegisterNewDispatcher(typeof(EventDispatcher));\n        }\n\n        /// <summary>\n        /// Register new event type\n        /// </summary>\n        /// <param name=\"t\"></param>\n        public void RegisterNewDispatcher(Type t)\n        { \n            if (dispatchers.ContainsKey(t)) return;\n            else dispatchers.Add(t, new Dispatcher());\n            return;\n        }\n\n        /// <summary>\n        /// Get all binder of this event\n        /// </summary>\n        /// <typeparam name=\"Event\">Target event</typeparam>\n        /// <param name=\"eventType\">Event dispatcher</param>\n        /// <returns></returns>\n        public HandlerList GetHandlerList<Event>(Type eventType)\n        {\n            return GetHandlerList(eventType, typeof(Event));\n        }\n\n        /// <summary>\n        /// Get all binder of this event\n        /// </summary>\n        /// <param name=\"eventType\">Event dispatcher</param>\n        /// <param name=\"event\">Target event</param>\n        /// <returns></returns>\n        public HandlerList GetHandlerList(Type eventType, Type @event)\n        {\n            return GetDispatcher(eventType)[@event];\n        }\n\n        /// <summary>\n        /// Get dispathcer by type\n        /// </summary>\n        /// <param name=\"eventType\">type</param>\n        /// <returns></returns>\n        public Dispatcher GetDispatcher(Type eventType)\n        {\n            return (dispatchers[eventType]);\n        }\n\n        /// <summary>\n        /// Get dispathcer by T\n        /// </summary>\n        /// <typeparam name=\"EventType\">Type</typeparam>\n        /// <returns></returns>\n        public Dispatcher GetDispatcher<EventType>()\n        {\n            return (dispatchers[typeof(EventType)]);\n        }\n\n        /// <summary>\n        /// Return a dispathcer is or not exist\n        /// </summary>\n        /// <typeparam name=\"EventType\"></typeparam>\n        /// <returns></returns>\n        public bool ExistDispatcher<EventType>()\n        {\n            return dispatchers.ContainsKey(typeof(EventType));\n        }\n\n        /// <summary>\n        /// Return a dispathcer is or not exit\n        /// </summary>\n        /// <param name=\"eventType\"></param>\n        /// <returns></returns>\n        public bool ExistDispatcher(Type eventType)\n        {\n            return dispatchers.ContainsKey(eventType);\n        }\n\n        /// <summary>\n        /// Fire event with async call\n        /// </summary>\n        /// <typeparam name=\"EventType\">Dispathcer</typeparam>\n        /// <typeparam name=\"Event\">Event</typeparam>\n        /// <param name=\"event\">Event instance</param>\n        internal void RaiseEventAsync<EventType, Event>(Event @event)  where Event : IBaseEvent\n        {\n            RaiseEventAsync(typeof(EventType), @event);\n        }\n\n        /// <summary>\n        /// Fire event with async call\n        /// </summary>\n        /// <typeparam name=\"Event\">Event</typeparam>\n        /// <param name=\"eventType\">dispatcher</param>\n        /// <param name=\"event\">event instance</param>\n        internal void RaiseEventAsync<Event>(Type eventType, Event @event) where Event : IBaseEvent\n        {\n            if (!GetDispatcher(eventType).ContainsKey(typeof(Event))) return;\n            foreach (var item in GetDispatcher(eventType)[typeof(Event)])\n            {\n                var p = ((EventHandlerFunc<Event>)(item));\n                Task.Run(() => p((@event)));\n            }\n            \n        }\n\n        /// <summary>\n        /// Fire event with sync call\n        /// </summary>\n        /// <typeparam name=\"EventType\">Event dispathcer</typeparam>\n        /// <typeparam name=\"Event\">Event</typeparam>\n        /// <param name=\"event\">Event instance</param>\n        internal void RaiseEvent<EventType, Event>(Event @event) where Event : IBaseEvent\n        {\n            RaiseEvent(typeof(EventType), @event);\n        }\n\n        /// <summary>\n        /// Fire event with sync call\n        /// </summary>\n        /// <typeparam name=\"Event\">Event</typeparam>\n        /// <param name=\"eventType\">dispatcher</param>\n        /// <param name=\"event\">event instance</param>\n        internal void RaiseEvent<Event>(Type eventType, Event @event) where Event : IBaseEvent\n        {\n            Type typo = typeof(Event);\n            if (!GetDispatcher(eventType).ContainsKey(typo)) return;\n            foreach (var item in GetDispatcher(eventType)[typo])\n            {\n                ((EventHandlerFunc<Event>)(item))(@event);\n            }\n        }\n\n        /// <summary>\n        /// Register event handler\n        /// </summary>\n        /// <typeparam name=\"Event\">Target event</typeparam>\n        /// <param name=\"eventType\">Dispatcher</param>\n        /// <param name=\"handler\">handler</param>\n        /// <returns></returns>\n        public bool RegisterEventHandler<Event>(Type eventType, EventHandlerFunc<Event> handler) where Event : IBaseEvent\n        {\n            Type typo = typeof(Event);\n            Dispatcher dispatcher = null;\n            if (ExistDispatcher(eventType))\n            {\n                dispatcher = GetDispatcher(eventType);\n            }\n            else\n            {\n                throw new Exception(\"Dispatcher not register!\");\n            }\n            \n            if(!dispatcher.ContainsKey(typo))\n            {\n                dispatcher.Add(typo, new HandlerList());\n            }\n            if (dispatcher[typo].Contains(handler)) return false;\n            dispatcher[typo].AddLast(handler);\n            return true;\n        }\n\n        /// <summary>\n        /// Register event handle\n        /// </summary>\n        /// <typeparam name=\"EventType\">Event dispathcer</typeparam>\n        /// <typeparam name=\"Event\">Target Event</typeparam>\n        /// <param name=\"handler\">Handler</param>\n        /// <returns></returns>\n        public bool RegisterEventHandler<EventType, Event>(EventHandlerFunc<Event> handler) where Event : IBaseEvent\n        {\n            return RegisterEventHandler(typeof(EventType), handler);\n        }\n\n        /// <summary>\n        /// Remove event handle\n        /// </summary>\n        /// <typeparam name=\"EventType\">Event dispathcer</typeparam>\n        /// <typeparam name=\"Event\">Target Event</typeparam>\n        /// <param name=\"handler\">Handler</param>\n        public void RemoveEventHandler<EventType, Event>(EventHandlerFunc<Event> handler) where Event : IBaseEvent\n        {\n            if (dispatchers.TryGetValue(typeof(EventType),out Dispatcher dispatcher))\n            {\n                var list = dispatcher[typeof(Event)];\n                list.Remove(handler);\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "Sync/MessageFilter/FilterBase.cs",
    "content": "﻿using System;\nusing Sync.Source;\nusing Sync.Tools;\nusing static Sync.Tools.DefaultI18n;\nusing Sync.Plugins;\n\nnamespace Sync.MessageFilter\n{\n    /// <summary>\n    /// Base filter message\n    /// </summary>\n    public interface IMessageBase : IBaseEvent\n    {\n        StringElement User { get; set; }\n        StringElement Message { get; set; }\n        bool Cancel { get; set; }\n    }\n\n    /// <summary>\n    /// Online change event will raise this message\n    /// </summary>\n    public struct OnlineChangeMessage : IMessageBase\n    {\n        public StringElement User { get; set; }\n        public StringElement Message { get; set; }\n        public StringElement Name { get; set; }\n        public int Count { get; set; }\n        public bool Cancel { get; set; }\n\n        public OnlineChangeMessage(int count)\n        {\n            User = \"\";\n            Message = string.Format(LANG_Current_Online, count);\n            Name = \"\";\n            Cancel = false;\n            this.Count = count;\n        }\n    }\n\n    /// <summary>\n    /// GiftMessage event will raise this message\n    /// </summary>\n    public struct GiftMessage : IMessageBase\n    {\n        public StringElement User { get; set; }\n        public StringElement Message { get; set; }\n        public StringElement Name { get; set; }\n        public IBaseGiftEvent Source { get; }\n        public int Count { get; set; }\n        /// <summary>\n        /// cancel标志指示了这条来自弹幕的消息将不会同步到IRC。\n        /// </summary>\n        public bool Cancel { get; set; }\n\n        public GiftMessage(IBaseGiftEvent src)\n        {\n            this.User = src.SenderName;\n            this.Name = src.GiftName;\n            this.Count = src.GiftCount;\n            this.Source = src;\n            this.Message = string.Format(LANG_Gift_Sent, Count, Name);\n            Cancel = false;\n        }\n    }\n\n    /// <summary>\n    /// Normal danmaku event will raise this message\n    /// </summary>\n    public struct DanmakuMessage : IMessageBase\n    {\n        public StringElement User { get; set; }\n        public StringElement Message { get; set; }\n        /// <summary>\n        /// cancel标志指示了这条来自弹幕的消息将不会同步到IRC。\n        /// </summary>\n        public bool Cancel { get; set; }\n\n        public DanmakuMessage(IBaseDanmakuEvent src)\n        {\n            this.User = src.SenderName;\n            this.Message = src.Danmuku;\n            this.Cancel = false;\n        }\n    }\n\n    /// <summary>\n    /// Client Reccive event will raise this message\n    /// </summary>\n    public struct IRCMessage : IMessageBase\n    {\n        public StringElement User { get; set; }\n        public StringElement Message { get; set; }\n        /// <summary>\n        /// cancel标志指示了这条来自IRC的消息，不会同步到弹幕\n        /// </summary>\n        public bool Cancel { get; set; }\n\n        public IRCMessage(StringElement user, StringElement rawMessage)\n        {\n            this.User = user;\n            this.Message = rawMessage;\n            this.Cancel = false;\n        }\n    }\n\n    public interface ISourceOnlineChange\n    {\n        //just flag\n    }\n\n    public interface ISourceGift\n    {\n        //just flag\n    }\n\n    public interface ISourceDanmaku\n    {\n        //just flag\n    }\n\n    public interface ISourceClient \n    {\n        //just flag\n    }\n\n    public interface IFilter\n    {\n        void onMsg(ref IMessageBase msg);\n\n    }\n}\n"
  },
  {
    "path": "Sync/MessageFilter/FilterManager.cs",
    "content": "﻿using Sync.MessageFilter;\r\nusing Sync.Source;\r\nusing Sync.Tools;\r\nusing System;\r\nusing System.Text;\r\nusing System.Collections.Generic;\r\nusing System.Linq;\r\nnamespace Sync.Plugins\r\n{\r\n    /// <summary>\r\n    /// Manager and filter message\r\n    /// </summary>\r\n    public class FilterManager : BaseEventDispatcher<IMessageBase>\r\n    {\r\n        Dictionary<Type, List<IFilter>> filters;\r\n\r\n        internal FilterManager()\r\n        {\r\n            filters = new Dictionary<Type, List<IFilter>>();\r\n\r\n            AddSource<ISourceClient>();\r\n            AddSource<ISourceDanmaku>();\r\n            AddSource<ISourceOnlineChange>();\r\n            AddSource<ISourceGift>();\r\n\r\n            EventDispatcher.Instance.RegisterNewDispatcher(GetType());\r\n\r\n\r\n            //Bind source event for Message Dispathcer\r\n            SourceEvents.Instance.BindEvent<IBaseDanmakuEvent>(evt => SyncHost.Instance.Messages.RaiseMessage<ISourceDanmaku>(new IRCMessage(evt.SenderName, evt.Danmuku)));\r\n            \r\n            SourceEvents.Instance.BindEvent<BaseOnlineCountEvent>(evt =>\r\n            {\r\n                if (DefaultConfiguration.Instance.EnableViewersChangedNotify.ToBool())\r\n                    SyncHost.Instance.Messages.RaiseMessage<ISourceOnlineChange>(new OnlineChangeMessage(evt.Count));\r\n            });\r\n\r\n            SourceEvents.Instance.BindEvent<IBaseGiftEvent>(evt =>\r\n            {\r\n                if (DefaultConfiguration.Instance.EnableGiftChangedNotify.ToBool())\r\n                    SyncHost.Instance.Messages.RaiseMessage<ISourceClient>(new GiftMessage(evt));\r\n            });\r\n        }\r\n\r\n        private void AddSource<T>()\r\n        {\r\n            filters.Add(typeof(T), new List<IFilter>());\r\n        }\r\n\r\n        public IEnumerable<KeyValuePair<Type, IFilter>> GetFiltersEnum()\r\n        {\r\n            foreach (var list in filters)\r\n            {\r\n                foreach(var item in list.Value)\r\n                {\r\n                    yield return new KeyValuePair<Type, IFilter>(list.Key, item);\r\n                }\r\n            }\r\n        }\r\n        \r\n        public int Count { get { return filters.Sum(p => p.Value.Count); } }\r\n\r\n        internal void PassFilterDanmaku(ref IMessageBase msg)\r\n        {\r\n            PassFilter<ISourceDanmaku>(ref msg);\r\n        }\r\n\r\n        internal void PassFilterOSU(ref IMessageBase msg)\r\n        {\r\n            PassFilter<ISourceClient>(ref msg);\r\n        }\r\n\r\n        internal void PassFilterGift(ref IMessageBase msg)\r\n        {\r\n            PassFilter<ISourceGift>(ref msg);\r\n        }\r\n\r\n        internal void PassFilterOnlineChange(ref IMessageBase msg)\r\n        {\r\n            PassFilter<ISourceOnlineChange>(ref msg);\r\n        }\r\n\r\n        private void PassFilter<T>(ref IMessageBase msg)\r\n        {\r\n            PassFilter(typeof(T), ref msg);\r\n        }\r\n\r\n        private void PassFilter(Type identify, ref IMessageBase msg)\r\n        {\r\n            foreach (var filter in filters[identify])\r\n            {\r\n                filter.onMsg(ref msg);\r\n\r\n                if (msg.Cancel) //已经处理的就不用继续给后面的处理了\r\n                    break;\r\n            }\r\n\r\n            RaiseEventAsync(msg);\r\n        }\r\n\r\n        public void AddFilter(IFilter filter)\r\n        {\r\n            foreach (var i in filter.GetType().GetInterfaces())\r\n            {\r\n                if(filters.ContainsKey(i))\r\n                {\r\n                    filters[i].Add(filter);\r\n                    filters[i].Sort((a,b)=> ((int)b.GetFilterPriority())- ((int)a.GetFilterPriority()));\r\n                }\r\n            }\r\n        }\r\n\r\n        public void deleteFilter(IFilter filter)\r\n        {\r\n            foreach (var i in filter.GetType().GetInterfaces())\r\n            {\r\n                if (filters.ContainsKey(i))\r\n                {\r\n                    filters[i].Remove(filter);\r\n                }\r\n            }\r\n        }\r\n\r\n        public void AddFilters(params IFilter[] filters)\r\n        {\r\n            foreach (IFilter filter in filters)\r\n            {\r\n                AddFilter(filter);\r\n            }\r\n        }\r\n    }\r\n\r\n}\r\n"
  },
  {
    "path": "Sync/MessageFilter/FilterPriorityAttribute.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace Sync.MessageFilter\n{\n    public enum FilterPriority\n    {\n        Lowest=-2,\n        Low=-1,\n        Normal=0,\n        High=1,\n        Highest=2\n    }\n\n    [AttributeUsage(AttributeTargets.Class)]\n    public class FilterPriorityAttribute:Attribute\n    {\n        public FilterPriority Priority { get; set; } = FilterPriority.Normal;\n    }\n\n    public static class FilterPriorityAttributeExtension\n    {\n        public static FilterPriority GetFilterPriority(this IFilter filter)\n        {\n            var attr=filter.GetType().GetCustomAttributes(typeof(FilterPriorityAttribute),false).FirstOrDefault() as FilterPriorityAttribute;\n\n            return attr?.Priority ?? FilterPriority.Normal;\n        }\n    }\n}\n"
  },
  {
    "path": "Sync/MessageFilter/MessageDispatcher.cs",
    "content": "﻿using Sync.Client;\nusing Sync.MessageFilter;\nusing Sync.Source;\nusing Sync.Tools;\nusing System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace Sync.Plugins\n{\n    public class MessageDispatcher\n    {\n        FilterManager filters;\n\n        internal MessageDispatcher(FilterManager f)\n        {\n            filters = f;\n            MessageManager.LimitLevel = 3;\n            MessageManager.Option = MessageManager.PeekOption.Auto;\n            MessageManager.Init(filters);\n            MessageManager.SetSendMessageAction(new MessageManager.SendMessageAction((target,message) =>{\n                    SyncHost.Instance.ClientWrapper.Client.SendMessage(new IRCMessage(target, message));\n            }));\n\n        }\n        /// <summary>\n        /// Send danmaku message to osu!IRC\n        /// </summary>\n        /// <param name=\"danmaku\">Danmaku</param>\n        public void onDanmaku(IBaseDanmakuEvent danmaku)\n        {\n            IMessageBase msg = new DanmakuMessage(danmaku);\n            RaiseMessage<ISourceDanmaku>(msg);\n        }\n\n        /// <summary>\n        /// Send osu!irc message to damaku\n        /// </summary>\n        /// <param name=\"user\">Sender</param>\n        /// <param name=\"message\">Message</param>\n        public void onIRC(StringElement user, StringElement message)\n        {\n            IMessageBase msg = new IRCMessage(user, message);\n            RaiseMessage<ISourceClient>(msg);\n        }\n\n\n\n        public void RaiseMessage<Source>(IMessageBase msg)\n        {\n            RaiseMessage(typeof(Source), msg);\n        }\n\n        /// <summary>\n        /// Raise a message and pass to filter list\n        /// </summary>\n        private void RaiseMessage(Type msgType, IMessageBase msg)\n        {\n            IMessageBase newMsg = msg;\n\n            //From danmaku\n            if (msgType == typeof(ISourceDanmaku))\n            {\n                filters.PassFilterDanmaku(ref newMsg);\n                if (newMsg.Cancel) return;\n                else\n                {\n                    //Send this danmaku message to osu!irc\n                    MessageManager.PostIRCMessage(SyncHost.Instance.ClientWrapper.Client.NickName, newMsg);\n                }\n                return;\n            }\n\n            //From osu!orc\n            else if (msgType == typeof(ISourceClient))\n            {\n                filters.PassFilterOSU(ref newMsg);\n                if (newMsg.Cancel) return;\n                else\n                {\n                    //Detect sender is osu!irc self\n                    if (newMsg.User.RawText == SyncHost.Instance.ClientWrapper.Client.NickName)\n                    {\n                        //Send message to danmaku source\n                        if (!SyncHost.Instance.SourceWrapper.Sendable)\n                        {\n                            IO.CurrentIO.WriteColor(DefaultI18n.LANG_SendNotReady, ConsoleColor.Red);\n                        }\n                        else\n                        {\n                            SyncHost.Instance.SourceWrapper.SendableSource.Send(newMsg);\n                        }\n                    }\n                    //Send message to irc if Not sender\n                    else\n                    {\n                        MessageManager.PostIRCMessage(SyncHost.Instance.ClientWrapper.Client.NickName, newMsg);\n                    }\n\n                }\n                return;\n            }\n\n            //From danmaku gift(subscribe or other gift)\n            else if (msgType == typeof(ISourceGift))\n            {\n                filters.PassFilterGift(ref newMsg);\n                if (newMsg.Cancel) return;\n                else\n                {\n                    SyncHost.Instance.ClientWrapper.Client?.SendMessage(new IRCMessage(SyncHost.Instance.ClientWrapper.Client.NickName, newMsg.Message));\n                }\n            }\n\n            //The spectator count change\n            else if (msgType == typeof(ISourceOnlineChange))\n            {\n                filters.PassFilterOnlineChange(ref newMsg);\n                if (newMsg.Cancel) return;\n                else\n                {\n                    SyncHost.Instance.ClientWrapper.Client?.SendMessage(new IRCMessage(SyncHost.Instance.ClientWrapper.Client.NickName, newMsg.Message));\n                }\n            }\n\n        }\n    }\n}\n"
  },
  {
    "path": "Sync/MessageFilter/MessageManager.cs",
    "content": "﻿using Sync.Plugins;\nusing Sync.Tools;\nusing System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading;\n\nnamespace Sync.MessageFilter\n{\n    /// <summary>\n    /// Message limiter\n    /// </summary>\n    public class MessageManager\n    {\n        private static float sendLimit_pre = 0;\n\n        private static int time_inv = 1000;\n        private static float time = 0;\n\n        private static float recoverTime = 60000;\n\n        private static SendFilter sendFilter = new SendFilter();\n\n        public static PeekOption Option { set; get; } = PeekOption.Auto;\n\n        private static Thread thread = null;\n\n        public static float RecoverTime\n        {\n            get { return recoverTime; }\n        }\n\n        public static int CurrentQueueCount\n        {\n            get { return MessageQueue.Count; }\n        }\n\n        private static float currentRecoverTime = 0;\n\n        private static Object lockObj = new object();\n\n        private static volatile int sendCount = 0;\n\n        public int CurrentCount\n        {\n            get\n            {\n                return sendCount;\n            }\n        }\n\n        private static int limitCount = 5;\n\n        public static int LimitLevel\n        {\n            get { return limitCount; }\n            set\n            {\n                limitCount = value;\n                sendLimit_pre = (float)limitCount / (60000 / time_inv);\n            }\n        }\n\n        private static volatile bool isLimit = false;\n\n        public static bool IsLimit\n        {\n            get\n            {\n                return isLimit;\n            }\n        }\n\n        private static SendMessageAction action;\n        \n        private static List<MessageBaseWrapper> MessageQueue = new List<MessageBaseWrapper>();\n\n        private struct MessageBaseWrapper\n        {\n            public DateTime time;\n            public string target;\n            public IMessageBase message;\n        }\n\n        private class SendFilter : IFilter, ISourceDanmaku\n        {\n            public void onMsg(ref IMessageBase msg)\n            {\n                switch (Option)\n                {\n                    case PeekOption.ForceAll://还要过滤可能存在的?前缀\n                        break;\n\n                    case PeekOption.DisableAll:\n                        msg.Cancel = true;\n                        return;\n\n                    case PeekOption.OnlySendCommand:\n                        if (!msg.Message.RawText.StartsWith(\"?send\"))\n                        {\n                            msg.Cancel = true;\n                            return;\n                        }\n                        break;\n\n                    case PeekOption.Auto:\n                        if (IsLimit && (!msg.Message.RawText.StartsWith(\"?send\")))\n                        {\n                            msg.Cancel = true;\n                            return;\n                        }\n                        break;\n\n                    default:\n                        break;\n                }\n\n                if (msg.Message.RawText.StartsWith(\"?send \"))\n                    msg.Message = new StringElement(msg.Message.RawText.Remove(0, 5));\n            }\n        }\n\n        public enum PeekOption\n        {\n            ForceAll,\n            DisableAll,\n            OnlySendCommand,\n            Auto\n        }\n\n        public static void Init(FilterManager manager)\n        {\n            thread = new Thread(runThread);\n            SyncHost.Instance.Filters.AddFilter(sendFilter);\n            thread.Start();\n        }\n        \n        public static void PostIRCMessage(string target, IMessageBase message)\n        {\n            lock (lockObj)\n            {\n                MessageQueue.Add(new MessageBaseWrapper() {\n                    time=DateTime.Now,\n                    target=target,\n                    message =message\n                });\n\n                sendCount++;\n            }\n        }\n\n        public static void SetOption(string optionName)\n        {\n            if (Enum.TryParse(optionName, true, out PeekOption peekOption))\n                Option = peekOption;\n            else\n                Option = PeekOption.Auto;\n        }\n\n        public delegate void SendMessageAction(string userName, string message);\n\n        public static void SetSendMessageAction(SendMessageAction action)\n        {\n            MessageManager.action = action;\n        }\n\n        public static void SendMessage(string userName, string message)\n        {\n            action(userName, message);\n        }\n\n        private static void runThread(object state)\n        {\n            StringBuilder sb=new StringBuilder();\n\n            while (true)\n            {\n                Thread.Sleep(time_inv);\n\n                sb.Clear();\n                \n                time = time + time_inv;\n\n                if (Option == PeekOption.Auto)\n                {\n                    //Exceeded limit?\n                    if (!isLimit && (float)sendCount / (60000.0f / time_inv) >= sendLimit_pre)\n                    {\n                        isLimit = true;\n                        IO.CurrentIO.WriteColor(\"isLimit is true now\", ConsoleColor.Yellow);\n                        currentRecoverTime = 0;\n                    }\n\n                    //unlimit time\n                    currentRecoverTime += isLimit ? time_inv : 0;\n                    if (currentRecoverTime >= recoverTime)\n                    {\n                        currentRecoverTime = 0;\n                        isLimit = false;\n                        IO.CurrentIO.WriteColor(\"isLimit is false now\", ConsoleColor.Yellow);\n                    }\n                }\n\n                sendCount = 0;\n\n                if (isLimit)\n                {\n                    lock (lockObj)\n                    {\n                        while (MessageQueue.Count != 0)\n                        {\n                            var message = MessageQueue[0];\n                            MessageQueue.RemoveAt(0);\n                            if (message.message == null)\n                                break;\n                            sb.AppendFormat(\"{0}:{1} || \", message.message.User, message.message.Message.RawText);\n                        }\n                    }\n\n                    if (sb.Length != 0)\n                        SendMessage(\"\", sb.ToString());\n                }\n                else\n                {\n                    if (MessageQueue.Count != 0)\n                    {\n                        var message = MessageQueue[0];\n                        MessageQueue.RemoveAt(0);\n                        SendMessage(message.target, message.message.User + message.message.Message.ToString());\n                    }\n                }\n            }\n        }\n    }\n}"
  },
  {
    "path": "Sync/Plugins/Plugin.cs",
    "content": "﻿using Sync.Command;\nusing System;\nusing System.Reflection;\n\nnamespace Sync.Plugins\n{\n    public abstract class Plugin\n    {\n        public readonly string Name;\n        public readonly string Author;\n        public BaseEventDispatcher<IPluginEvent> EventBus { get => PluginEvents.Instance; }\n\n        public Plugin(string Name, string Author)\n        {\n            this.Name = Name;\n            this.Author = Author;\n        }\n\n        protected SyncHost getHoster()\n        {\n            return SyncHost.Instance;\n        }\n\n        public string getName()\n        {\n            return Name;\n        }\n\n        public string getAuthor()\n        {\n            return Author;\n        }\n\n        public string getGuid()\n        {\n            return this.GetType().GetCustomAttribute<SyncPluginID>()?.GUID;\n        }\n\n        public override string ToString()\n        {\n            return Name;\n        }\n\n        public virtual void OnDisable()\n        {\n\n        }\n\n        public virtual void OnEnable()\n        {\n\n        }\n\n        public virtual void OnExit()\n        {\n\n        }\n    }\n}\n"
  },
  {
    "path": "Sync/Plugins/PluginManager.cs",
    "content": "﻿using Sync.Client;\nusing Sync.Command;\nusing Sync.Source;\nusing Sync.Tools;\nusing System;\nusing System.Collections.Generic;\nusing System.IO;\nusing System.Linq;\nusing System.Reflection;\nusing static Sync.Tools.DefaultI18n;\n\nnamespace Sync.Plugins\n{\n    /// <summary>\n    /// A flag for plugin event type\n    /// </summary>\n    public interface IPluginEvent : IBaseEvent { }\n\n    /// <summary>\n    /// Base plugin events\n    /// </summary>\n    public class PluginEvents : BaseEventDispatcher<IPluginEvent>\n    {\n        /// <summary>\n        /// Fire when init plugin\n        /// </summary>\n        public struct InitPluginEvent : IPluginEvent\n        {\n            public Plugin Plugin { get; private set; }\n\n            public InitPluginEvent(Plugin plugin)\n            {\n                this.Plugin = plugin;\n            }\n        }\n\n        /// <summary>\n        /// Fire when init source\n        /// </summary>\n        public struct InitSourceEvent : IPluginEvent\n        {\n            public SourceManager Sources { get; private set; }\n\n            public InitSourceEvent(SourceManager source)\n            {\n                Sources = source;\n            }\n        }\n\n        /// <summary>\n        /// Fire when init filter\n        /// </summary>\n        public struct InitFilterEvent : IPluginEvent\n        {\n            public FilterManager Filters { get; private set; }\n\n            public InitFilterEvent(FilterManager filters)\n            {\n                Filters = filters;\n            }\n        }\n\n        /// <summary>\n        /// Fire when init command\n        /// </summary>\n        public struct InitCommandEvent : IPluginEvent\n        {\n            public CommandManager Commands { get; private set; }\n\n            public InitCommandEvent(CommandManager commands)\n            {\n                Commands = commands;\n            }\n        }\n\n        /// <summary>\n        /// Fire when init clients\n        /// </summary>\n        public struct InitClientEvent : IPluginEvent\n        {\n            public ClientManager Clients { get; private set; }\n\n            public InitClientEvent(ClientManager clients)\n            {\n                Clients = clients;\n            }\n        }\n\n        /// <summary>\n        /// Fire when init source warpper\n        /// </summary>\n        public struct InitSourceWarpperEvent : IPluginEvent\n        {\n            public SourceWorkWrapper SourceWrapper { get; private set; }\n\n            public InitSourceWarpperEvent(SourceWorkWrapper wrapper)\n            {\n                SourceWrapper = wrapper;\n            }\n        }\n\n        /// <summary>\n        /// Fire when init client warpper\n        /// </summary>\n        public struct InitClientWarpperEvent : IPluginEvent\n        {\n            public ClientWorkWrapper ClientWrapper { get; private set; }\n\n            public InitClientWarpperEvent(ClientWorkWrapper wrapper)\n            {\n                ClientWrapper = wrapper;\n            }\n        }\n\n        /// <summary>\n        /// Fire when load complete\n        /// </summary>\n        public struct LoadCompleteEvent : IPluginEvent\n        {\n            public SyncHost Host { get; private set; }\n\n            public LoadCompleteEvent(SyncHost host)\n            {\n                Host = host;\n            }\n        }\n\n        public struct ConfigurationChange : IPluginEvent\n        {\n        }\n\n        /// <summary>\n        /// Fire when ready\n        /// </summary>\n        public struct ProgramReadyEvent : IPluginEvent\n        {\n            //public SyncManager Manager { get; private set; }\n            //public SyncManagerCompleteEvent()\n            //{\n            //    Manager = Program.host.SyncInstance;\n            //}\n        }\n\n        public static readonly PluginEvents Instance = new PluginEvents();\n\n        private PluginEvents()\n        {\n            EventDispatcher.Instance.RegisterNewDispatcher(GetType());\n        }\n    }\n\n    /// <summary>\n    /// Plugins Manager\n    /// <para>Load plugins from Plugins foldere and Initial plugin</para>\n    /// </summary>\n    public class PluginManager\n    {\n        private List<Plugin> pluginList;\n        private List<Assembly> asmList;\n        private LinkedList<Type> loadedList;\n        private List<Type> allList;\n\n        internal PluginManager()\n        {\n        }\n\n        /// <summary>\n        /// Raise global <see cref=\"PluginEvents.InitCommandEvent\"/> to all plugin\n        /// </summary>\n        /// <returns>Return commands count</returns>\n        internal int LoadCommnads()\n        {\n            PluginEvents.Instance.RaiseEvent(new PluginEvents.InitCommandEvent(SyncHost.Instance.Commands));\n            return SyncHost.Instance.Commands.Dispatch.count;\n        }\n\n        /// <summary>\n        /// Raise global <see cref=\"PluginEvents.InitSourceEvent\"/> to all plugin\n        /// </summary>\n        /// <returns>Return source count</returns>\n        internal int LoadSources()\n        {\n            PluginEvents.Instance.RaiseEvent(new PluginEvents.InitSourceEvent(SyncHost.Instance.Sources));\n            return SyncHost.Instance.Sources.SourceList.Count();\n        }\n\n        /// <summary>\n        /// Raise global <see cref=\"PluginEvents.InitFilterEvent\"/> to all plugin\n        /// </summary>\n        /// <returns>Return filter count</returns>\n        internal int LoadFilters()\n        {\n            PluginEvents.Instance.RaiseEvent(new PluginEvents.InitFilterEvent(SyncHost.Instance.Filters));\n            return SyncHost.Instance.Filters.Count;\n        }\n\n        /// <summary>\n        /// Raise global <see cref=\"PluginEvents.InitClientEvent\"/> to all plugin\n        /// </summary>\n        /// <returns></returns>\n        internal int LoadClients()\n        {\n            PluginEvents.Instance.RaiseEvent(new PluginEvents.InitClientEvent(SyncHost.Instance.Clients));\n            return SyncHost.Instance.Clients.Count;\n        }\n\n        /// <summary>\n        /// Raise global <see cref=\"PluginEvents.ProgramReadyEvent\"/> to all plugin\n        /// </summary>\n        internal void ReadySync()\n        {\n            PluginEvents.Instance.RaiseEvent(new PluginEvents.ProgramReadyEvent());\n        }\n\n        /// <summary>\n        /// Get a <see cref=\"IEnumerable{T}\"/> for plugin list\n        /// </summary>\n        /// <returns></returns>\n        public IEnumerable<Plugin> GetPlugins()\n        {\n            return pluginList;\n        }\n\n        /// <summary>\n        /// Internal get plugin list\n        /// </summary>\n        /// <returns></returns>\n        internal List<Plugin> GetPluginList()\n        {\n            return pluginList;\n        }\n\n        /// <summary>\n        /// Raise a <see cref=\"PluginEvents.LoadCompleteEvent\"/> to all plugin\n        /// </summary>\n        internal void ReadyProgram()\n        {\n            PluginEvents.Instance.RaiseEvent(new PluginEvents.LoadCompleteEvent(SyncHost.Instance));\n        }\n\n        /// <summary>\n        /// Initial and load all support Plugin in 'Plugins' folder\n        /// </summary>\n        /// <returns>Plugins count</returns>\n        internal int LoadPlugins()\n        {\n            //Combine search path\n            string path = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, \"Plugins\");\n\n            //Current plugins list\n            pluginList = new List<Plugin>();\n            //Loaded assembly\n            asmList = new List<Assembly>();\n\n            //Pre-add all assemblies in current AppDomain\n            asmList.AddRange(AppDomain.CurrentDomain.GetAssemblies());\n\n            //create Plugins folder if not exist\n            if (!Directory.Exists(path)) \n            {\n                try\n                {\n                    Directory.CreateDirectory(path);\n                    IO.CurrentIO.WriteColor($\"Created default Plugins folder : {path}\", ConsoleColor.Green);\n                }\n                catch (Exception e)\n                {\n                    IO.CurrentIO.WriteColor($\"Create default Plugins folder ({path}) failed : {e.Message} \", ConsoleColor.Yellow);\n                }\n            }\n\n            //Change directiory to Plugins\n            Directory.SetCurrentDirectory(path);\n\n            //create cache folder\n            var rootCache = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, \"cache\");\n            string cache = Path.Combine(rootCache, $\"cache_{(new Random()).Next().ToString(\"x8\")}\");\n\n            try\n            {\n                Directory.Delete(rootCache, true);\n                Directory.CreateDirectory(rootCache);\n            } catch { }\n\n            try\n            {\n                if (Directory.Exists(cache))\n                {\n                    Directory.Delete(cache, true);\n                }\n                Directory.CreateDirectory(cache);\n            }\n            catch (Exception e)\n            {\n                IO.CurrentIO.WriteColor($\"Create temporary cache folder ({cache}) failed : {e.Message} \", ConsoleColor.Yellow);\n                throw e;\n            }\n\n            new DirectoryInfo(rootCache)\n            {\n                Attributes = FileAttributes.Normal\n            };\n\n            //error extra notify mark\n            bool got_locked_error = false;\n\n            //Search all .dll files in directory(include sub directory)\n            foreach (string file in Directory.GetFiles(path, \"*.dll\", SearchOption.AllDirectories))\n            {\n                try\n                {\n                    if (asmList.Any(a => a.Location == file))\n                        continue;\n                    //Load assembly directly\n                    string temp = Path.Combine(cache, Path.GetFileName(file));\n                    File.Copy(file, temp);\n                    Assembly asm = Assembly.LoadFrom(temp);\n\n                    asmList.Add(asm);\n                }\n                catch (BadImageFormatException)\n                {\n                    /*\n                     * Do not output anything when the file is not a\n                     * .Net assembly.\n                     * This can reduces unnecessary output when\n                     * unmanaged assembly is needed.\n                     */\n                }\n                catch (Exception e)\n                {\n                    //Not a .NET Assembly DLL\n                    IO.CurrentIO.WriteColor(String.Format(LANG_LoadPluginErr, file, e.Message), ConsoleColor.Red);\n\n                    if (e.Message.Contains(\"0x80131515\"))\n                        got_locked_error = true;\n                }\n            }\n\n            if (got_locked_error)\n                IO.CurrentIO.WriteColor(String.Format(\"Opps,It seems your plugin dll files were locked by System.\\n\" +\n                               \"Please view https://osu.ppy.sh/forum/t/685031/start=37 and https://osu.ppy.sh/forum/t/685031/start=24 to solve problem.\"), ConsoleColor.Red);\n\n            loadedList = new LinkedList<Type>();\n\n            //To slove plugin dependency,\n            List<Type> lazylist = new List<Type>();\n            allList = new List<Type>();\n\n            //Load all plugins first\n\n            foreach (Assembly asm in asmList)\n            {\n                try\n                {\n                    foreach (Type item in asm.GetExportedTypes())\n                    {\n                        Type it = asm.GetType(item.FullName);\n                        if (it == null ||\n                            !it.IsClass || !it.IsPublic ||\n                            !typeof(Plugin).IsAssignableFrom(it) ||\n                            typeof(Plugin) == it)\n                            continue;\n                        allList.Add(it);\n                    }\n                }\n                catch (FileNotFoundException e)\n                {\n                    CheckUnknownDependency(e.FileName.Substring(0, e.FileName.IndexOf(\",\") - 1));\n                }\n                catch (Exception e)\n                {\n                    //Not up to date\n                    IO.CurrentIO.WriteColor(String.Format(LANG_LoadPluginErr, asm.FullName, e.Message), ConsoleColor.Red);\n                    continue;\n                }\n            }\n\n            lazylist = allList.ToList();\n            //looping add for resolve dependency\n            do\n            {\n                lazylist = layerLoader(lazylist);\n            } while (lazylist.Count != 0);\n\n            return pluginList.Count;\n        }\n\n        /// <summary>\n        /// Load plugins\n        /// <para>Load dependencies plugin first</para>\n        /// </summary>\n        /// <param name=\"asmList\">All <see cref=\"T\"/> : <see cref=\"Plugin\"/></param>\n        /// <returns>Not loaded plugins</returns>\n        private List<Type> layerLoader(IList<Type> asmList)\n        {\n            List<Type> nextLoad = new List<Type>();\n            foreach (Type it in asmList)\n            {\n                try\n                {\n                    var hardDeps = it.GetCustomAttributes<SyncPluginDependency>();\n\n                    foreach (var item in hardDeps)\n                    {\n                        var target = allList.Select(p => p.GetCustomAttribute<SyncPluginID>())?.FirstOrDefault(p => p?.GUID == item.GUID);\n                        if (item.Require && target == null) CheckGUIDUpdate(item);\n                        if (item.Version == null) continue;\n                        if (!CompareVersion(item.Version, target.Version)) CheckGUIDUpdate(item);\n                    }\n\n                    var softDeps = it.GetCustomAttributes<SyncSoftRequirePlugin>();\n                    foreach (var item in softDeps)\n                    {\n                        foreach (var dep in item.RequirePluguins)\n                        {\n                            if (!allList.Any(p => p.Name.Contains(dep) || dep.Contains(p.Name))) CheckUnknownDependency(dep);\n                        }\n                    }\n\n                    if (LateLoad(it))\n                    {\n#if (DEBUG)\n                        IO.CurrentIO.WriteColor($\"Lazy load [{it.Name}]\", ConsoleColor.Green);\n#endif\n                        nextLoad.Add(it);\n                        //Dependency load at this time\n                        //Lazy load this plugin at next time\n                        continue;\n                    }\n\n                    //no dependencies or dependencies all was loaded\n                    if (!it.IsSubclassOf(typeof(Plugin))) continue;\n                    else\n                    {\n                        LoadPluginFormType(it);\n                        loadedList.AddLast(it);\n                    }\n                }\n                catch (Exception e)\n                {\n                    IO.CurrentIO.WriteColor(String.Format(LANG_NotPluginErr, it.Name, e.Message), ConsoleColor.Red);\n                    IO.CurrentIO.WriteColor(e.StackTrace, ConsoleColor.Red);\n                    continue;\n                }\n            }\n\n            return nextLoad;\n        }\n\n        private bool LateLoad(Type a)\n        {\n            SyncRequirePlugin requireAttr = a.GetCustomAttribute<SyncRequirePlugin>();\n            SyncSoftRequirePlugin softRequirePlugin = a.GetCustomAttribute<SyncSoftRequirePlugin>();\n            IEnumerable<SyncPluginDependency> deps = a.GetCustomAttributes<SyncPluginDependency>();\n            SyncPluginID pid = a.GetCustomAttribute<SyncPluginID>();\n            if (deps != null)\n            {\n                foreach (var item in deps)\n                {\n                    if (loadedList.Any(p => p.GetCustomAttribute<SyncPluginID>()?.GUID == item.GUID)) continue;\n                    else\n                    {\n                        if (CheckIsReferenceTo(allList.FirstOrDefault(p => p.GetCustomAttribute<SyncPluginID>()?.GUID == item.GUID), pid?.GUID)) return false;\n                        else return true;\n                    }\n                }\n            }\n\n            if (requireAttr != null)\n            {\n                foreach (var item in requireAttr.RequirePluguins)\n                {\n                    //Dependency was been loaded\n                    if (loadedList.Contains(item)) continue;\n                    else\n                    {\n                        //Check cycle reference\n                        if (CheckIsReferenceTo(item, a)) return false;\n                        else return true;\n                    }\n                }\n            }\n\n            if (softRequirePlugin != null)\n            {\n                foreach (var item in softRequirePlugin.RequirePluguins)\n                {\n                    Type s = allList.FirstOrDefault(p => p.Name == item);\n                    if (s == null)\n                    {\n                        continue;\n                    }\n                    else\n                    {\n                        if (CheckIsReferenceTo(s, a)) return false;\n                        if (!loadedList.Contains(s)) return true;\n                    }\n                }\n            }\n\n            return false;\n        }\n\n        private bool CheckIsReferenceTo(Type a, string b)\n        {\n            var result = a.GetCustomAttributes<SyncPluginDependency>()?.Any(p => p.GUID == b);\n            if (result.HasValue) return result.Value;\n            else return false;\n        }\n\n        private bool CheckIsReferenceTo(Type a, Type b)\n        {\n            return CheckIsHardReferenceTo(a, b) || CheckIsSoftReferenceTo(a, b.Name);\n        }\n\n        private bool CheckIsHardReferenceTo(Type a, Type b)\n        {\n            SyncRequirePlugin refRequireCheck = a.GetCustomAttribute<SyncRequirePlugin>();\n            if (refRequireCheck == null) return false;\n            return refRequireCheck.RequirePluguins.Contains(b);\n        }\n\n        private bool CheckIsSoftReferenceTo(Type a, string b)\n        {\n            SyncSoftRequirePlugin refRequireCheck = a.GetCustomAttribute<SyncSoftRequirePlugin>();\n            if (refRequireCheck == null) return false;\n            return refRequireCheck.RequirePluguins.Contains(b);\n        }\n\n        /// <summary>\n        /// True if <paramref name=\"b\"/> is satisfy for the require of <paramref name=\"a\"/>\n        /// </summary>\n        /// <param name=\"a\">A version require</param>\n        /// <param name=\"b\">Target version</param>\n        /// <returns></returns>\n        public static bool CompareVersion(string a, string b)\n        {\n            // v : less or equal\n            // x : must\n            // ^ : large or equal\n            char start = a[0];\n\n            Func<bool?, bool> converter;\n            if (start == 'v') converter = p => p == null || p.Value;\n            else if (start == '^') converter = p => p == null || !p.Value;\n            else converter = p => p == null;\n\n            string ta = a.Substring(1);\n\n            var va = ta.Split('.').Select(k => int.Parse(k)).ToArray();\n            var vb = b.Split('.').Select(k => int.Parse(k)).ToArray();\n\n            for (int i = 0; i < va.Length; i++)\n            {\n                bool val = va[i] > vb[i];\n                if (va[i] == vb[i]) continue;\n                else return converter(val);\n            }\n            return converter(null);\n        }\n\n        private void CheckUnknownDependency(string name)\n        {\n            if (Updater.update.InstallByKeyword(name, false))\n            {\n                SyncHost.Instance.ForceRestartSync();\n                throw new SyncPluginOutdateException($\"Need restart application to update {name}\");\n            }\n            else\n            {\n                throw new SyncMissingPluginException($\"Can't install dependency plugin: {name}\");\n            }\n        }\n\n        private void CheckGUIDUpdate(SyncPluginDependency item)\n        {\n            if (Updater.update.InternalUpdate(item.GUID, true))\n            {\n                SyncHost.Instance.ForceRestartSync();\n                throw new SyncPluginOutdateException($\"Need restart application to update {item.GUID}\");\n            }\n            else\n            {\n                throw new SyncMissingPluginException($\"Can't install dependency plugin: {item.GUID}, version {item.Version}\");\n            }\n        }\n\n        private Plugin LoadPluginFormType(Type it)\n        {\n            object pluginTest = it.Assembly.CreateInstance(it.FullName);\n            if (pluginTest == null)\n            {\n                throw new NullReferenceException(\"Create instance fail!\");\n            }\n\n            Plugin plugin = (Plugin)pluginTest;\n            IO.CurrentIO.WriteColor(String.Format(LANG_LoadingPlugin, plugin.Name), ConsoleColor.White);\n\n            pluginList.Add(plugin);\n            plugin.OnEnable();\n            PluginEvents.Instance.RaiseEventAsync(new PluginEvents.InitPluginEvent(plugin));\n            return plugin;\n        }\n    }\n\n    public class SyncMissingPluginException : Exception\n    {\n        public SyncMissingPluginException(string msg) : base(msg)\n        {\n        }\n    }\n\n    public class SyncPluginOutdateException : Exception\n    {\n        public SyncPluginOutdateException(string msg) : base(msg)\n        {\n        }\n    }\n\n    /// <summary>\n    /// Using this attribute when you want load some plugin before your plugin.\n    /// </summary>\n    public class SyncRequirePlugin : Attribute\n    {\n        public IReadOnlyList<Type> RequirePluguins;\n\n        public SyncRequirePlugin(params Type[] types)\n        {\n            RequirePluguins = new List<Type>(types);\n        }\n    }\n\n    /// <summary>\n    /// Using this attribute when you dependence some plugin without hard link.\n    /// </summary>\n    public class SyncSoftRequirePlugin : Attribute\n    {\n        public IReadOnlyList<string> RequirePluguins;\n\n        public SyncSoftRequirePlugin(params string[] types)\n        {\n            RequirePluguins = new List<string>(types);\n        }\n    }\n\n    public class SyncPluginID : Attribute\n    {\n        public string GUID { get; }\n\n        /// <summary>\n        /// Major.Minjor.Reversion\n        /// <para>e.g: 1.4.5</para>\n        /// </summary>\n        public string Version { get; }\n\n        public SyncPluginID(string GUID, string Version)\n        {\n            this.Version = Version;\n            this.GUID = GUID;\n        }\n    }\n\n    public class SyncPluginDependency : Attribute\n    {\n        public string GUID { get; }\n        public string Version { get; set; }\n        public bool Require { get; set; }\n\n        public SyncPluginDependency(string guid) => GUID = guid;\n    }\n}\n"
  },
  {
    "path": "Sync/Program.cs",
    "content": "﻿using SharpRaven;\nusing Sync.Tools;\nusing System;\nusing System.Threading;\n\nnamespace Sync\n{\n    public static class Program\n    {\n        static void Main(string[] args)\n        {\n            AppDomain.CurrentDomain.UnhandledException += CurrentDomain_UnhandledException;\n            (new StartupHelper(args)).Start();\n        }\n\n        private static void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e)\n        {\n            SentryHelper.Instance.RepoterError(e.ExceptionObject as Exception);\n            Thread.Sleep(2000);\n            Environment.Exit(1);\n        }\n    }\n}\n"
  },
  {
    "path": "Sync/Properties/AssemblyInfo.cs",
    "content": "﻿using System.Reflection;\r\nusing System.Resources;\r\nusing System.Runtime.CompilerServices;\r\nusing System.Runtime.InteropServices;\r\nusing System.Windows;\r\n\r\n// 有关程序集的一般信息由以下\r\n// 控制。更改这些特性值可修改\r\n// 与程序集关联的信息。\r\n[assembly: AssemblyTitle(\"Sync!\")]\r\n[assembly: AssemblyDescription(\"Sync Live danmaku and Games!\")]\r\n[assembly: AssemblyConfiguration(\"\")]\r\n[assembly: AssemblyCompany(\"Remilia\")]\r\n[assembly: AssemblyProduct(\"Sync\")]\r\n[assembly: AssemblyCopyright(\"MIT\")]\r\n[assembly: AssemblyTrademark(\"RemiliaScarlet.com\")]\r\n[assembly: AssemblyCulture(\"\")]\r\n\r\n//将 ComVisible 设置为 false 将使此程序集中的类型\r\n//对 COM 组件不可见。  如果需要从 COM 访问此程序集中的类型，\r\n//请将此类型的 ComVisible 特性设置为 true。\r\n[assembly: ComVisible(false)]\r\n\r\n//若要开始生成可本地化的应用程序，请\r\n//<PropertyGroup> 中的 .csproj 文件中\r\n//例如，如果您在源文件中使用的是美国英语，\r\n//使用的是美国英语，请将 <UICulture> 设置为 en-US。  然后取消\r\n//对以下 NeutralResourceLanguage 特性的注释。  更新\r\n//以下行中的“en-US”以匹配项目文件中的 UICulture 设置。\r\n\r\n//[assembly: NeutralResourcesLanguage(\"en-US\", UltimateResourceFallbackLocation.Satellite)]\r\n\r\n\r\n[assembly: ThemeInfo(\r\n    ResourceDictionaryLocation.None, //主题特定资源词典所处位置\r\n                                     //(当资源未在页面\r\n                                     //或应用程序资源字典中找到时使用)\r\n    ResourceDictionaryLocation.SourceAssembly //常规资源词典所处位置\r\n                                              //(当资源未在页面\r\n                                              //、应用程序或任何主题专用资源字典中找到时使用)\r\n)]\r\n\r\n\r\n// 程序集的版本信息由下列四个值组成: \r\n//\r\n//      主版本\r\n//      次版本\r\n//      生成号\r\n//      修订号\r\n//\r\n//可以指定所有这些值，也可以使用“生成号”和“修订号”的默认值，\r\n// 方法是按如下所示使用“*”: :\r\n// [assembly: AssemblyVersion(\"1.0.*\")]\r\n[assembly: AssemblyVersion(\"2.18.5.0\")]\r\n[assembly: AssemblyFileVersion(\"2.18.5.0\")]\r\n"
  },
  {
    "path": "Sync/Properties/Resources.Designer.cs",
    "content": "﻿//------------------------------------------------------------------------------\n// <auto-generated>\n//     此代码由工具生成。\n//     运行时版本:4.0.30319.42000\n//\n//     对此文件的更改可能会导致不正确的行为，并且如果\n//     重新生成代码，这些更改将会丢失。\n// </auto-generated>\n//------------------------------------------------------------------------------\n\nnamespace Sync.Properties {\n    using System;\n    \n    \n    /// <summary>\n    ///   一个强类型的资源类，用于查找本地化的字符串等。\n    /// </summary>\n    // 此类是由 StronglyTypedResourceBuilder\n    // 类通过类似于 ResGen 或 Visual Studio 的工具自动生成的。\n    // 若要添加或移除成员，请编辑 .ResX 文件，然后重新运行 ResGen\n    // (以 /str 作为命令选项)，或重新生成 VS 项目。\n    [global::System.CodeDom.Compiler.GeneratedCodeAttribute(\"System.Resources.Tools.StronglyTypedResourceBuilder\", \"4.0.0.0\")]\n    [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]\n    [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]\n    internal class Resources {\n        \n        private static global::System.Resources.ResourceManager resourceMan;\n        \n        private static global::System.Globalization.CultureInfo resourceCulture;\n        \n        [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute(\"Microsoft.Performance\", \"CA1811:AvoidUncalledPrivateCode\")]\n        internal Resources() {\n        }\n        \n        /// <summary>\n        ///   返回此类使用的缓存的 ResourceManager 实例。\n        /// </summary>\n        [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]\n        internal static global::System.Resources.ResourceManager ResourceManager {\n            get {\n                if (object.ReferenceEquals(resourceMan, null)) {\n                    global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager(\"Sync.Properties.Resources\", typeof(Resources).Assembly);\n                    resourceMan = temp;\n                }\n                return resourceMan;\n            }\n        }\n        \n        /// <summary>\n        ///   使用此强类型资源类，为所有资源查找\n        ///   重写当前线程的 CurrentUICulture 属性。\n        /// </summary>\n        [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]\n        internal static global::System.Globalization.CultureInfo Culture {\n            get {\n                return resourceCulture;\n            }\n            set {\n                resourceCulture = value;\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "Sync/Properties/Resources.resx",
    "content": "﻿<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<root>\n  <!-- \n    Microsoft ResX Schema \n    \n    Version 2.0\n    \n    The primary goals of this format is to allow a simple XML format \n    that is mostly human readable. The generation and parsing of the \n    various data types are done through the TypeConverter classes \n    associated with the data types.\n    \n    Example:\n    \n    ... ado.net/XML headers & schema ...\n    <resheader name=\"resmimetype\">text/microsoft-resx</resheader>\n    <resheader name=\"version\">2.0</resheader>\n    <resheader name=\"reader\">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>\n    <resheader name=\"writer\">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>\n    <data name=\"Name1\"><value>this is my long string</value><comment>this is a comment</comment></data>\n    <data name=\"Color1\" type=\"System.Drawing.Color, System.Drawing\">Blue</data>\n    <data name=\"Bitmap1\" mimetype=\"application/x-microsoft.net.object.binary.base64\">\n        <value>[base64 mime encoded serialized .NET Framework object]</value>\n    </data>\n    <data name=\"Icon1\" type=\"System.Drawing.Icon, System.Drawing\" mimetype=\"application/x-microsoft.net.object.bytearray.base64\">\n        <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>\n        <comment>This is a comment</comment>\n    </data>\n                \n    There are any number of \"resheader\" rows that contain simple \n    name/value pairs.\n    \n    Each data row contains a name, and value. The row also contains a \n    type or mimetype. Type corresponds to a .NET class that support \n    text/value conversion through the TypeConverter architecture. \n    Classes that don't support this are serialized and stored with the \n    mimetype set.\n    \n    The mimetype is used for serialized objects, and tells the \n    ResXResourceReader how to depersist the object. This is currently not \n    extensible. For a given mimetype the value must be set accordingly:\n    \n    Note - application/x-microsoft.net.object.binary.base64 is the format \n    that the ResXResourceWriter will generate, however the reader can \n    read any of the formats listed below.\n    \n    mimetype: application/x-microsoft.net.object.binary.base64\n    value   : The object must be serialized with \n            : System.Serialization.Formatters.Binary.BinaryFormatter\n            : and then encoded with base64 encoding.\n    \n    mimetype: application/x-microsoft.net.object.soap.base64\n    value   : The object must be serialized with \n            : System.Runtime.Serialization.Formatters.Soap.SoapFormatter\n            : and then encoded with base64 encoding.\n\n    mimetype: application/x-microsoft.net.object.bytearray.base64\n    value   : The object must be serialized into a byte array \n            : using a System.ComponentModel.TypeConverter\n            : and then encoded with base64 encoding.\n    -->\n  <xsd:schema id=\"root\" xmlns=\"\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" xmlns:msdata=\"urn:schemas-microsoft-com:xml-msdata\">\n    <xsd:element name=\"root\" msdata:IsDataSet=\"true\">\n      <xsd:complexType>\n        <xsd:choice maxOccurs=\"unbounded\">\n          <xsd:element name=\"metadata\">\n            <xsd:complexType>\n              <xsd:sequence>\n                <xsd:element name=\"value\" type=\"xsd:string\" minOccurs=\"0\" />\n              </xsd:sequence>\n              <xsd:attribute name=\"name\" type=\"xsd:string\" />\n              <xsd:attribute name=\"type\" type=\"xsd:string\" />\n              <xsd:attribute name=\"mimetype\" type=\"xsd:string\" />\n            </xsd:complexType>\n          </xsd:element>\n          <xsd:element name=\"assembly\">\n            <xsd:complexType>\n              <xsd:attribute name=\"alias\" type=\"xsd:string\" />\n              <xsd:attribute name=\"name\" type=\"xsd:string\" />\n            </xsd:complexType>\n          </xsd:element>\n          <xsd:element name=\"data\">\n            <xsd:complexType>\n              <xsd:sequence>\n                <xsd:element name=\"value\" type=\"xsd:string\" minOccurs=\"0\" msdata:Ordinal=\"1\" />\n                <xsd:element name=\"comment\" type=\"xsd:string\" minOccurs=\"0\" msdata:Ordinal=\"2\" />\n              </xsd:sequence>\n              <xsd:attribute name=\"name\" type=\"xsd:string\" msdata:Ordinal=\"1\" />\n              <xsd:attribute name=\"type\" type=\"xsd:string\" msdata:Ordinal=\"3\" />\n              <xsd:attribute name=\"mimetype\" type=\"xsd:string\" msdata:Ordinal=\"4\" />\n            </xsd:complexType>\n          </xsd:element>\n          <xsd:element name=\"resheader\">\n            <xsd:complexType>\n              <xsd:sequence>\n                <xsd:element name=\"value\" type=\"xsd:string\" minOccurs=\"0\" msdata:Ordinal=\"1\" />\n              </xsd:sequence>\n              <xsd:attribute name=\"name\" type=\"xsd:string\" use=\"required\" />\n            </xsd:complexType>\n          </xsd:element>\n        </xsd:choice>\n      </xsd:complexType>\n    </xsd:element>\n  </xsd:schema>\n  <resheader name=\"resmimetype\">\n    <value>text/microsoft-resx</value>\n  </resheader>\n  <resheader name=\"version\">\n    <value>2.0</value>\n  </resheader>\n  <resheader name=\"reader\">\n    <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>\n  </resheader>\n  <resheader name=\"writer\">\n    <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>\n  </resheader>\n</root>"
  },
  {
    "path": "Sync/Properties/Settings.Designer.cs",
    "content": "﻿//------------------------------------------------------------------------------\n// <auto-generated>\n//     此代码由工具生成。\n//     运行时版本:4.0.30319.42000\n//\n//     对此文件的更改可能会导致不正确的行为，并且如果\n//     重新生成代码，这些更改将会丢失。\n// </auto-generated>\n//------------------------------------------------------------------------------\n\nnamespace Sync.Properties {\n    \n    \n    [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]\n    [global::System.CodeDom.Compiler.GeneratedCodeAttribute(\"Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator\", \"14.0.0.0\")]\n    internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase {\n        \n        private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings())));\n        \n        public static Settings Default {\n            get {\n                return defaultInstance;\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "Sync/Properties/Settings.settings",
    "content": "﻿<?xml version='1.0' encoding='utf-8'?>\n<SettingsFile xmlns=\"uri:settings\" CurrentProfile=\"(Default)\">\n  <Profiles>\n    <Profile Name=\"(Default)\" />\n  </Profiles>\n  <Settings />\n</SettingsFile>"
  },
  {
    "path": "Sync/Resources/App.config",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<configuration>\n    <startup> \n        \n    <supportedRuntime version=\"v4.0\" sku=\".NETFramework,Version=v4.5\"/></startup>\n</configuration>\n"
  },
  {
    "path": "Sync/Source/SourceBase.cs",
    "content": "﻿using Sync.MessageFilter;\nusing Sync.Plugins;\nusing Sync.Tools;\nusing System;\nusing System.Collections;\nusing System.Collections.Generic;\nusing System.Threading.Tasks;\n\nnamespace Sync.Source\n{\n\n    /// <summary>\n    /// Flag this source is support send\n    /// </summary>\n    public abstract class SendableSource : SourceBase\n    {\n        //if sendablesource is able send message now.\n        public virtual bool SendStatus { get => Status == SourceStatus.CONNECTED_WORKING; }\n\n        public SendableSource(string Name, string Author) : base(Name, Author)\n        {\n\n        }\n\n        internal void login(string user, string password)\n        {\n            Login(user, password);\n        }\n\n        public abstract void Login(string user, string password);\n        public abstract void Send(IMessageBase message);\n    }\n\n    /// <summary>\n    /// base class help program manager the source impl in program dispatch\n    /// </summary>\n    public abstract class SourceBase\n    {\n\n        public string Name { get; private set; }\n        public string Author { get; private set; }\n        public string LiveID { get; set; } = \"\";\n        public BaseEventDispatcher<ISourceEvent> EventBus { get => SourceEvents.Instance; }\n        public SourceStatus Status { get; protected set; }\n        \n\n        public SourceBase(string Name, string Author)\n        {\n            this.Name = Name;\n            this.Author = Author;\n            this.Status = SourceStatus.IDLE;\n        }\n\n        /// <summary>\n        /// Raise a event synchronized and dispatch it to handler asynchronized\n        /// </summary>\n        /// <typeparam name=\"T\">Target Event Type</typeparam>\n        /// <param name=\"args\">Type args</param>\n        protected void RaiseEvent<T>(T args) where T : ISourceEvent\n        {\n            EventBus.RaiseEvent(args);\n        }\n\n        internal void connect()\n        {\n            this.Status = SourceStatus.USER_REQUEST_CONNECT;\n            Connect();\n        }\n\n        internal void disconnect()\n        {\n            this.Status = SourceStatus.USER_REQUEST_DISCONNECT;\n            Disconnect();\n        }\n\n        public abstract void Connect();\n        public abstract void Disconnect();\n\n    }\n\n    ///// <summary>\n    ///// fire when Source event raised.\n    ///// </summary>\n    ///// <param name=\"args\"></param>\n    ////public delegate void SourceEventEvt<T>(T args) where T : SourceEvent;\n\n    ///// <summary>\n    ///// Source event base arg class\n    ///// Including event name and eventobject\n    ///// </summary>\n    //public class SourceEventArgs<T> where T : SourceEvent\n    //{\n    //    private SourceEvent eventObj;\n    //    public string Name { get; private set; }\n    //    public T EventObject { get => (T)eventObj; }\n    //    public SourceEventArgs(T EventObject)\n    //    {\n    //        eventObj = EventObject;\n    //    }\n\n    //    public T CastTo()\n    //    {\n    //        return (T)EventObject;\n    //    }\n    //}\n\n    /// <summary>\n    /// Source network impossible status\n    /// </summary>\n    public enum SourceStatus\n    {\n        /// <summary>\n        /// Source working good, still working\n        /// </summary>\n        CONNECTED_WORKING,\n        /// <summary>\n        /// Source working good, but waiting for remote server\n        /// </summary>\n        CONNECTED_WAITING,\n        /// <summary>\n        /// Still establish connection to target server\n        /// </summary>\n        CONNECTING,       \n        /// <summary>\n        /// disconnect by remote\n        /// </summary>\n        REMOTE_DISCONNECTED,\n        /// <summary>\n        /// disconnect by user or network drop\n        /// </summary>\n        USER_DISCONNECTED,\n        /// <summary>\n        /// no any connection action\n        /// </summary>\n        IDLE,\n        /// <summary>\n        /// user request connect and waiting for Source class response.\n        /// </summary>\n        USER_REQUEST_CONNECT,\n        /// <summary>\n        /// user request disconnect, and waiting for Source class response.\n        /// </summary>\n        USER_REQUEST_DISCONNECT,\n    }\n\n}\n"
  },
  {
    "path": "Sync/Source/SourceEvent.cs",
    "content": "﻿using Sync.Client;\nusing Sync.MessageFilter;\nusing Sync.Plugins;\nusing System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace Sync.Source\n{\n\n    /// <summary>\n    /// Singleton for source events\n    /// </summary>\n    public sealed class SourceEvents : BaseEventDispatcher<ISourceEvent>\n    {\n        public readonly static SourceEvents Instance = new SourceEvents();\n\n        private SourceEvents()\n        {\n            EventDispatcher.Instance.RegisterNewDispatcher(GetType());\n        }\n    }\n\n    /// <summary>\n    /// This message will fire when source start work\n    /// </summary>\n    public struct StartSourceEvent : ISourceEvent\n    {\n        public SourceWorkWrapper Source { get => SyncHost.Instance.SourceWrapper; }\n    }\n\n    /// <summary>\n    /// This message will fire when source stop work\n    /// </summary>\n    public struct StopSyncEvent : ISourceEvent\n    {\n\n    }\n\n    /// <summary>\n    /// The message will fire when source recive a danmaku\n    /// </summary>\n    public struct BaseDanmakuEvent : IBaseDanmakuEvent\n    {\n        public string Danmuku { get; set; }\n        public string SenderName { get; set; }\n        public string SendTime { get; set; }\n\n        public BaseDanmakuEvent(string danmaku, string sender, string time)\n        {\n            Danmuku = danmaku;\n            SenderName = sender;\n            SendTime = time;\n        }\n    }\n\n    /// <summary>\n    /// Base danmaku interface\n    /// </summary>\n    public interface IBaseDanmakuEvent : ISourceEvent\n    {\n        string Danmuku { get; set; }\n        string SenderName { get; set; }\n        string SendTime { get; set; }\n    }\n\n    /// <summary>\n    /// Base gift interface\n    /// </summary>\n    public interface IBaseGiftEvent : ISourceEvent\n    {\n        string GiftName { get; set; }\n        int GiftCount { get; set; }\n        string SenderName { get; set; }\n        string SendTime { get; set; }\n\n    }\n\n    /// <summary>\n    /// This event will fire when source status change\n    /// </summary>\n    public struct BaseStatusEvent : ISourceEvent\n    {\n        public SourceStatus Status { get; private set; }\n\n        public BaseStatusEvent(SourceStatus status)\n        {\n            Status = status;\n        }\n    }\n\n    /// <summary>\n    /// This event will fire when source check online change\n    /// </summary>\n    public struct BaseOnlineCountEvent : ISourceEvent\n    {\n        public int Count { get; set; }\n\n        public BaseOnlineCountEvent(int Count)\n        {\n            this.Count = Count;\n        }\n    }\n\n    /// <summary>\n    /// Source event flag\n    /// </summary>\n    public interface ISourceEvent : IBaseEvent\n    {\n    }\n}\n"
  },
  {
    "path": "Sync/Source/SourceManager.cs",
    "content": "﻿using Sync.Source;\nusing System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace Sync.Plugins\n{\n    /// <summary>\n    /// Source manager\n    /// </summary>\n    public class SourceManager\n    {\n        LinkedList<SourceBase> listSources;\n\n\n        public SourceManager()\n        {\n            listSources = new LinkedList<SourceBase>();\n        }\n\n        public IEnumerable<SourceBase> SourceList\n        {\n            get\n            {\n                return listSources;\n            }\n        }\n\n        public bool AddSource(SourceBase src)\n        {\n            if(listSources.Contains(src))\n            {\n                return false;\n            }\n            else\n            {\n                listSources.AddLast(src);\n            }\n            return true;\n        }\n    }\n}\n"
  },
  {
    "path": "Sync/Source/SourceWorkWrapper.cs",
    "content": "﻿using System;\r\nusing System.Collections.Generic;\r\nusing System.Linq;\r\nusing System.Text;\r\nusing System.Threading.Tasks;\r\nusing Sync.Plugins;\r\nusing Sync.Tools;\r\nusing static Sync.Tools.DefaultI18n;\r\n\r\nnamespace Sync.Source\r\n{\r\n    /// <summary>\r\n    /// A wrapper for select source and check source senable\r\n    /// </summary>\r\n    public class SourceWorkWrapper\r\n    {\r\n        private SourceManager sources;\r\n        public bool Sendable { get; } \r\n\r\n        public SourceWorkWrapper(SourceManager sources)\r\n        {\r\n            this.sources = sources;\r\n            Source = sources.SourceList.Where(p => p.Name == DefaultConfiguration.Instance.Source).FirstOrDefault()??sources.SourceList.FirstOrDefault();/*没有的话就默认第一个*/\r\n            if(Source == null)\r\n            {\r\n                IO.CurrentIO.WriteColor(LANG_NO_ANY_SOURCE, ConsoleColor.Red);\r\n            }\r\n            if (Source is SendableSource)\r\n            {\r\n                SendableSource = (SendableSource)Source;\r\n                Sendable = true;\r\n            }\r\n        }\r\n\r\n        public SourceBase Source { get; internal set; }\r\n        public SendableSource SendableSource { get; internal set; }\r\n    }\r\n}\r\n "
  },
  {
    "path": "Sync/Sync.csproj",
    "content": "﻿<?xml version=\"1.0\" encoding=\"utf-8\"?>\r\n<Project ToolsVersion=\"14.0\" DefaultTargets=\"Build\" xmlns=\"http://schemas.microsoft.com/developer/msbuild/2003\">\r\n  <Import Project=\"$(MSBuildExtensionsPath)\\$(MSBuildToolsVersion)\\Microsoft.Common.props\" Condition=\"Exists('$(MSBuildExtensionsPath)\\$(MSBuildToolsVersion)\\Microsoft.Common.props')\" />\r\n  <PropertyGroup>\r\n    <Configuration Condition=\" '$(Configuration)' == '' \">Debug</Configuration>\r\n    <Platform Condition=\" '$(Platform)' == '' \">AnyCPU</Platform>\r\n    <ProjectGuid>{FBD514C2-2830-479E-B050-D1C383028456}</ProjectGuid>\r\n    <OutputType>Exe</OutputType>\r\n    <AppDesignerFolder>Properties</AppDesignerFolder>\r\n    <RootNamespace>Sync</RootNamespace>\r\n    <AssemblyName>Sync</AssemblyName>\r\n    <TargetFrameworkVersion>v4.5</TargetFrameworkVersion>\r\n    <FileAlignment>512</FileAlignment>\r\n    <ProjectTypeGuids>{60dc8134-eba5-43b8-bcc9-bb4bc16c2548};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>\r\n    <WarningLevel>4</WarningLevel>\r\n    <TargetFrameworkProfile />\r\n    <IsWebBootstrapper>false</IsWebBootstrapper>\r\n    <PublishUrl>publish\\</PublishUrl>\r\n    <Install>true</Install>\r\n    <InstallFrom>Disk</InstallFrom>\r\n    <UpdateEnabled>false</UpdateEnabled>\r\n    <UpdateMode>Foreground</UpdateMode>\r\n    <UpdateInterval>7</UpdateInterval>\r\n    <UpdateIntervalUnits>Days</UpdateIntervalUnits>\r\n    <UpdatePeriodically>false</UpdatePeriodically>\r\n    <UpdateRequired>false</UpdateRequired>\r\n    <MapFileExtensions>true</MapFileExtensions>\r\n    <ApplicationRevision>0</ApplicationRevision>\r\n    <ApplicationVersion>1.0.0.%2a</ApplicationVersion>\r\n    <UseApplicationTrust>false</UseApplicationTrust>\r\n    <BootstrapperEnabled>true</BootstrapperEnabled>\r\n  </PropertyGroup>\r\n  <PropertyGroup Condition=\" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' \">\r\n    <PlatformTarget>x86</PlatformTarget>\r\n    <DebugSymbols>true</DebugSymbols>\r\n    <DebugType>full</DebugType>\r\n    <Optimize>false</Optimize>\r\n    <OutputPath>bin\\Debug\\</OutputPath>\r\n    <DefineConstants>TRACE;DEBUG</DefineConstants>\r\n    <ErrorReport>prompt</ErrorReport>\r\n    <WarningLevel>4</WarningLevel>\r\n    <Prefer32Bit>true</Prefer32Bit>\r\n  </PropertyGroup>\r\n  <PropertyGroup Condition=\" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' \">\r\n    <PlatformTarget>x86</PlatformTarget>\r\n    <DebugType>none</DebugType>\r\n    <Optimize>true</Optimize>\r\n    <OutputPath>..\\Release\\</OutputPath>\r\n    <DefineConstants>TRACE;SyncRelease</DefineConstants>\r\n    <ErrorReport>prompt</ErrorReport>\r\n    <WarningLevel>0</WarningLevel>\r\n    <Prefer32Bit>true</Prefer32Bit>\r\n    <AllowUnsafeBlocks>false</AllowUnsafeBlocks>\r\n    <CodeAnalysisIgnoreGeneratedCode>true</CodeAnalysisIgnoreGeneratedCode>\r\n    <GenerateSerializationAssemblies>Off</GenerateSerializationAssemblies>\r\n  </PropertyGroup>\r\n  <PropertyGroup>\r\n    <StartupObject>Sync.Program</StartupObject>\r\n  </PropertyGroup>\r\n  <PropertyGroup>\r\n    <ApplicationIcon>Resources\\SyncIcon.ico</ApplicationIcon>\r\n  </PropertyGroup>\r\n  <PropertyGroup>\r\n    <TargetZone>LocalIntranet</TargetZone>\r\n  </PropertyGroup>\r\n  <PropertyGroup>\r\n    <GenerateManifests>false</GenerateManifests>\r\n  </PropertyGroup>\r\n  <PropertyGroup />\r\n  <ItemGroup>\r\n    <Reference Include=\"Newtonsoft.Json, Version=6.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL\">\r\n      <HintPath>..\\packages\\Newtonsoft.Json.6.0.8\\lib\\net45\\Newtonsoft.Json.dll</HintPath>\r\n    </Reference>\r\n    <Reference Include=\"SharpRaven, Version=2.4.0.0, Culture=neutral, processorArchitecture=MSIL\">\r\n      <HintPath>..\\packages\\SharpRaven.2.4.0\\lib\\net45\\SharpRaven.dll</HintPath>\r\n    </Reference>\r\n    <Reference Include=\"System\" />\r\n    <Reference Include=\"System.Configuration\" />\r\n    <Reference Include=\"System.Data\" />\r\n    <Reference Include=\"System.IO.Compression\" />\r\n    <Reference Include=\"System.IO.Compression.FileSystem\" />\r\n    <Reference Include=\"System.Runtime.InteropServices.RuntimeInformation, Version=4.0.1.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL\">\r\n      <HintPath>..\\packages\\System.Runtime.InteropServices.RuntimeInformation.4.3.0\\lib\\net45\\System.Runtime.InteropServices.RuntimeInformation.dll</HintPath>\r\n    </Reference>\r\n    <Reference Include=\"System.Runtime.Serialization\" />\r\n    <Reference Include=\"System.Xml\" />\r\n    <Reference Include=\"Microsoft.CSharp\" />\r\n    <Reference Include=\"System.Core\" />\r\n    <Reference Include=\"System.Xml.Linq\" />\r\n    <Reference Include=\"System.Data.DataSetExtensions\" />\r\n    <Reference Include=\"System.Net.Http\" />\r\n    <Reference Include=\"System.Xaml\">\r\n      <RequiredTargetFramework>4.0</RequiredTargetFramework>\r\n    </Reference>\r\n    <Reference Include=\"PresentationCore\" />\r\n    <Reference Include=\"PresentationFramework\" />\r\n  </ItemGroup>\r\n  <ItemGroup>\r\n    <Compile Include=\"Client\\ClientEvent.cs\" />\r\n    <Compile Include=\"Client\\ClientManager.cs\" />\r\n    <Compile Include=\"Client\\ClientWorkWrapper.cs\" />\r\n    <Compile Include=\"Client\\DefaultReciveClient.cs\" />\r\n    <Compile Include=\"Command\\CommandManager.cs\" />\r\n    <Compile Include=\"MessageFilter\\FilterBase.cs\" />\r\n    <Compile Include=\"Event\\EventDispatcher.cs\" />\r\n    <Compile Include=\"MessageFilter\\FilterManager.cs\" />\r\n    <Compile Include=\"MessageFilter\\FilterPriorityAttribute.cs\" />\r\n    <Compile Include=\"MessageFilter\\MessageDispatcher.cs\" />\r\n    <Compile Include=\"MessageFilter\\MessageManager.cs\" />\r\n    <Compile Include=\"Tools\\SentryHelper.cs\" />\r\n    <Compile Include=\"Tools\\StartupArgument.cs\" />\r\n    <Compile Include=\"Tools\\StartupHelper.cs\" />\r\n    <Compile Include=\"Tools\\Builtin\\CommonCommand.cs\" />\r\n    <Compile Include=\"Tools\\ConfigurationAttribute\\BaseConfigurationAttribute.cs\" />\r\n    <Compile Include=\"Tools\\ConfigurationAttribute\\ClientAndSourceAttribute.cs\" />\r\n    <Compile Include=\"Tools\\CommandParser.cs\" />\r\n    <Compile Include=\"Tools\\Builtin\\PluginCommand.cs\" />\r\n    <Compile Include=\"Tools\\Builtin\\InternalPlugin.cs\" />\r\n    <Compile Include=\"Tools\\ConfigurationAttribute\\ConfigurationHolderAttribute.cs\" />\r\n    <Compile Include=\"Tools\\SyncIO\\FileLoggerWriter.cs\" />\r\n    <Compile Include=\"Tools\\SyncIO\\IOWrapper.cs\" />\r\n    <Compile Include=\"Tools\\SyncIO\\NConsoleWriter.cs\" />\r\n    <Compile Include=\"Tools\\Updater.cs\" />\r\n    <Compile Include=\"Plugins\\Plugin.cs\" />\r\n    <Compile Include=\"Plugins\\PluginManager.cs\" />\r\n    <Compile Include=\"Source\\SourceManager.cs\" />\r\n    <Compile Include=\"Source\\SourceEvent.cs\" />\r\n    <Compile Include=\"Source\\SourceWorkWrapper.cs\" />\r\n    <Compile Include=\"SyncHost.cs\" />\r\n    <Compile Include=\"Source\\SourceBase.cs\" />\r\n    <Compile Include=\"Program.cs\" />\r\n    <Compile Include=\"Command\\CommandDispatch.cs\" />\r\n    <Compile Include=\"Tools\\ConfigurationAttribute\\BoolAttribute.cs\" />\r\n    <Compile Include=\"Tools\\ConfigurationAttribute\\ColorAttribute.cs\" />\r\n    <Compile Include=\"Tools\\ConfigurationAttribute\\GuiLanguageElement.cs\" />\r\n    <Compile Include=\"Tools\\ConfigurationAttribute\\FloatAttribute.cs\" />\r\n    <Compile Include=\"Tools\\ConfigurationAttribute\\FontAttribute.cs\" />\r\n    <Compile Include=\"Tools\\ConfigurationAttribute\\IntegerAttribute.cs\" />\r\n    <Compile Include=\"Tools\\ConfigurationAttribute\\ListAttribute.cs\" />\r\n    <Compile Include=\"Tools\\ConfigurationAttribute\\PathAttribute.cs\" />\r\n    <Compile Include=\"Tools\\ConfigurationAttribute\\StringAttribute.cs\" />\r\n    <Compile Include=\"Tools\\Configuration.cs\" />\r\n    <Compile Include=\"Tools\\ConfigurationIO.cs\" />\r\n    <Compile Include=\"Tools\\SyncIO\\ConsoleWriter.cs\" />\r\n    <Compile Include=\"Tools\\I18n.cs\" />\r\n    <Compile Include=\"Tools\\IConfigurable.cs\" />\r\n    <Compile Include=\"Tools\\SyncIO\\Logger.cs\" />\r\n    <Compile Include=\"Tools\\PluginConfiuration.cs\" />\r\n    <Compile Include=\"Tools\\StringElement.cs\" />\r\n    <Compile Include=\"Tools\\Utils\\ConfigurationHelper.cs\" />\r\n    <Compile Include=\"Tools\\Utils\\PluginsHelper.cs\" />\r\n  </ItemGroup>\r\n  <ItemGroup>\r\n    <Compile Include=\"Properties\\AssemblyInfo.cs\">\r\n      <SubType>Code</SubType>\r\n    </Compile>\r\n    <Compile Include=\"Properties\\Resources.Designer.cs\">\r\n      <AutoGen>True</AutoGen>\r\n      <DesignTime>True</DesignTime>\r\n      <DependentUpon>Resources.resx</DependentUpon>\r\n    </Compile>\r\n    <Compile Include=\"Properties\\Settings.Designer.cs\">\r\n      <AutoGen>True</AutoGen>\r\n      <DependentUpon>Settings.settings</DependentUpon>\r\n      <DesignTimeSharedInput>True</DesignTimeSharedInput>\r\n    </Compile>\r\n    <EmbeddedResource Include=\"Properties\\Resources.resx\">\r\n      <Generator>ResXFileCodeGenerator</Generator>\r\n      <LastGenOutput>Resources.Designer.cs</LastGenOutput>\r\n    </EmbeddedResource>\r\n    <None Include=\"app.config\" />\r\n    <None Include=\"packages.config\" />\r\n    <None Include=\"Properties\\Settings.settings\">\r\n      <Generator>SettingsSingleFileGenerator</Generator>\r\n      <LastGenOutput>Settings.Designer.cs</LastGenOutput>\r\n    </None>\r\n    <AppDesigner Include=\"Properties\\\" />\r\n    <None Include=\"config.ini\" />\r\n  </ItemGroup>\r\n  <ItemGroup>\r\n    <None Include=\"Resources\\App.config\">\r\n      <SubType>Designer</SubType>\r\n    </None>\r\n  </ItemGroup>\r\n  <ItemGroup>\r\n    <Resource Include=\"Resources\\SyncIcon.ico\" />\r\n  </ItemGroup>\r\n  <ItemGroup>\r\n    <BootstrapperPackage Include=\".NETFramework,Version=v4.5\">\r\n      <Visible>False</Visible>\r\n      <ProductName>Microsoft .NET Framework 4.5 %28x86 和 x64%29</ProductName>\r\n      <Install>true</Install>\r\n    </BootstrapperPackage>\r\n    <BootstrapperPackage Include=\"Microsoft.Net.Framework.3.5.SP1\">\r\n      <Visible>False</Visible>\r\n      <ProductName>.NET Framework 3.5 SP1</ProductName>\r\n      <Install>false</Install>\r\n    </BootstrapperPackage>\r\n  </ItemGroup>\r\n  <Import Project=\"$(MSBuildToolsPath)\\Microsoft.CSharp.targets\" />\r\n  <PropertyGroup>\r\n    <PostBuildEvent>xcopy $(ProjectDir)\\..\\Language $(ProjectDir)\\$(OutDir)\\Language\\ /s/e/y</PostBuildEvent>\r\n  </PropertyGroup>\r\n  <!-- To modify your build process, add your task inside one of the targets below and uncomment it. \n       Other similar extension points exist, see Microsoft.Common.targets.\n  <Target Name=\"BeforeBuild\">\n  </Target>\n  <Target Name=\"AfterBuild\">\n  </Target>\n  -->\r\n</Project>"
  },
  {
    "path": "Sync/SyncHost.cs",
    "content": "﻿using Sync.Command;\nusing Sync.Plugins;\nusing Sync.Tools;\nusing System;\nusing System.Collections.Generic;\nusing static Sync.Tools.IO;\nusing static Sync.Tools.DefaultI18n;\nusing Sync.Client;\nusing Sync.Source;\nusing System.Reflection;\nusing System.Diagnostics;\n\nnamespace Sync\n{\n    /// <summary>\n    /// A manager for global modules\n    /// </summary>\n    public class SyncHost\n    {\n        public static SyncHost Instance { get; internal set; }\n\n        private SourceWorkWrapper sourceWrapper;\n        private ClientWorkWrapper clientWrapper;\n        private CommandManager commands;\n        private PluginManager plugins;\n        private SourceManager sources;\n        private FilterManager filters;\n        private MessageDispatcher messages;\n        private ClientManager clients;\n        /// <summary>\n        /// Internal use only\n        /// </summary>\n        internal SyncHost()\n        {\n\n        }\n\n        /// <summary>\n        /// Invoke to load plugins\n        /// </summary>\n        internal void Load()\n        {\n            CurrentIO.Write(LANG_Loading);\n\n            //Initial plugins manager\n            plugins = new PluginManager();\n            CurrentIO.WriteColor(String.Format(LANG_Plugins, plugins.LoadPlugins()), ConsoleColor.Green);\n\n            //Initial danmaku source\n            sources = new SourceManager();\n            CurrentIO.WriteColor(String.Format(LANG_Sources, plugins.LoadSources()), ConsoleColor.Green);\n\n            //select a danmaku source by config\n            try\n            {\n                sourceWrapper = new SourceWorkWrapper(sources);\n                PluginEvents.Instance.RaiseEvent(new PluginEvents.InitSourceWarpperEvent(sourceWrapper));\n            }\n            catch\n            {\n                CurrentIO.Write(\"\");\n                CurrentIO.WriteColor(LANG_Error, ConsoleColor.Red);\n                CurrentIO.WriteColor(\"Press enter to continue\", ConsoleColor.Red);\n                CurrentIO.ReadCommand();\n            }\n\n            //Get clients singleton\n            clients = ClientManager.Instance;\n            CurrentIO.WriteColor(String.Format(LANG_Client, plugins.LoadClients()), ConsoleColor.Green);\n\n            clientWrapper = new ClientWorkWrapper(clients);\n            PluginEvents.Instance.RaiseEvent(new PluginEvents.InitClientWarpperEvent(clientWrapper));\n\n            commands = new CommandManager();\n            CurrentIO.WriteColor(String.Format(LANG_Commands, plugins.LoadCommnads()), ConsoleColor.Green);\n\n            filters = new FilterManager();\n            CurrentIO.WriteColor(String.Format(LANG_Filters, plugins.LoadFilters()), ConsoleColor.Green);\n\n            messages = new MessageDispatcher(filters);\n\n            plugins.ReadyProgram();\n\n            CurrentIO.WriteColor(LANG_Ready, ConsoleColor.Cyan);\n        }\n\n        /// <summary>\n        /// The internal PluginManager instance property\n        /// </summary>\n        internal PluginManager Plugins { get { return plugins; } }\n\n        /// <summary>\n        /// Read only plugins list\n        /// </summary>\n        /// <returns></returns>\n        public IEnumerable<Plugin> EnumPluings()\n        {\n            return plugins.GetPlugins();\n        }\n\n        public CommandManager Commands\n        {\n            get\n            {\n                return commands;\n            }\n        }\n\n        public SourceManager Sources\n        {\n            get\n            {\n                return sources;\n            }\n        }\n\n        public FilterManager Filters\n        {\n            get\n            {\n                return filters;\n            }\n        }\n\n        public MessageDispatcher Messages\n        {\n            get { return messages; }\n        }\n\n        public ClientManager Clients { get => clients; }\n\n        public SourceWorkWrapper SourceWrapper { get => sourceWrapper; }\n\n        public ClientWorkWrapper ClientWrapper { get => clientWrapper; }\n\n        public void ExitSync()\n        {\n            try\n            {\n                SaveSync();\n            }\n            finally\n            {\n                Environment.Exit(0);\n            }\n        }\n\n        public void SaveSync()\n        {\n            ClientWrapper?.Client?.StopWork();\n            SourceWrapper?.Source?.Disconnect();\n\n            foreach (var item in PluginConfigurationManager.ConfigurationSet)\n            {\n                item.SaveAll();\n            }\n\n            plugins.GetPluginList().ForEach(p => p.OnExit());\n\n        }\n\n        public void RestartSync()\n        {\n            try\n            {\n                SaveSync();\n            }\n            finally\n            {\n                ForceRestartSync();\n            }\n        }\n\n        public void ForceRestartSync()\n        {\n            Process.Start(Assembly.GetEntryAssembly().Location);\n            Environment.Exit(0);\n        }\n    }\n}\n"
  },
  {
    "path": "Sync/Tools/Builtin/CommonCommand.cs",
    "content": "﻿using Sync.Command;\nusing Sync.MessageFilter;\nusing Sync.Plugins;\nusing Sync.Source;\nusing Sync.Tools;\nusing System;\nusing System.Collections.Generic;\nusing System.Diagnostics;\nusing System.Globalization;\nusing System.Linq;\nusing System.Reflection;\nusing static Sync.Tools.DefaultI18n;\n\nnamespace Sync.Tools.Builtin\n{\n    public sealed class CommonCommand\n    {\n        public void BindCommondCommand(CommandDispatch dispatch)\n        {\n            dispatch.bind(\"exit\", exit, LANG_COMMANDS_EXIT);\n            dispatch.bind(\"restart\", restart, LANG_COMMANDS_RESTART);\n            dispatch.bind(\"stop\", stop, LANG_COMMANDS_STOP);\n            dispatch.bind(\"start\", start, LANG_COMMANDS_START);\n            dispatch.bind(\"status\", status, LANG_COMMANDS_STATUS);\n            dispatch.bind(\"sourcemsg\", sourcemsg, LANG_COMMANDS_SOURCEMSG);\n            dispatch.bind(\"clientmsg\", clientmsg, LANG_COMMANDS_CLIENTMSG);\n\n            dispatch.bind(\"clientusermsg\", chatuser, LANG_COMMANDS_CLIENTUSERMSG);\n            dispatch.bind(\"disable\", disable, LANG_COMMANDS_DISABLE);\n            dispatch.bind(\"client\", switchclient, LANG_COMMANDS_SWITCH_CLIENT);\n            dispatch.bind(\"sourcelogin\", sourcelogin, LANG_COMMANDS_SOURCELOGIN);\n\n            dispatch.bind(\"clear\", clear, LANG_COMMANDS_CLEAR);\n\n            dispatch.bind(\"help\", help, LANG_COMMANDS_HELP);\n            dispatch.bind(\"listlang\", languages, LANG_COMMANDS_LISTLANG);\n\n            dispatch.bind(\"lang\", language, LANG_COMMANDS_LANG);\n            dispatch.bind(\"msgmgr\", msgmgr, LANG_COMMANDS_MSGMGR);\n            dispatch.bind(\"source\", listsource, LANG_COMMANDS_SOURCES);\n            dispatch.bind(\"filters\", filters, LANG_COMMANDS_FILTERS);\n        }\n\n        private bool listsource(Arguments arg)\n        {\n            foreach (SourceBase src in SyncHost.Instance.Sources.SourceList)\n            {\n                IO.CurrentIO.WriteColor(\"\", ConsoleColor.Gray, false);\n                IO.CurrentIO.WriteColor(LANG_COMMANDS_SOURCES_NAME, ConsoleColor.Cyan, false, false);\n                IO.CurrentIO.WriteColor(src.Name.PadRight(18), ConsoleColor.White, false, false);\n                IO.CurrentIO.WriteColor(LANG_COMMANDS_SOURCES_AUTHOR, ConsoleColor.DarkCyan, false, false);\n                IO.CurrentIO.WriteColor(src.Author, ConsoleColor.White, true, false);\n            }\n\n            IO.CurrentIO.WriteColor(string.Format(LANG_COMMANDS_CURRENT, SyncHost.Instance.ClientWrapper?.Client?.ClientName ?? \"还没指定接收源\"), ConsoleColor.Green);\n\n            return true;\n        }\n\n        private bool switchclient(Arguments arg)\n        {\n            if (arg.Count == 0)\n            {\n                foreach (var item in SyncHost.Instance.Clients.Clients)\n                {\n                    IO.CurrentIO.WriteColor(\"\", ConsoleColor.Gray, false);\n                    IO.CurrentIO.WriteColor(LANG_COMMANDS_CLIENT_NAME, ConsoleColor.Cyan, false, false);\n                    IO.CurrentIO.WriteColor(item.ClientName.PadRight(18), ConsoleColor.White, false, false);\n                    IO.CurrentIO.WriteColor(LANG_COMMANDS_CLIENT_AUTHOR, ConsoleColor.DarkCyan, false, false);\n                    IO.CurrentIO.WriteColor(item.Author, ConsoleColor.White, true, false);\n                }\n\n                IO.CurrentIO.WriteColor(string.Format(LANG_COMMANDS_CURRENT, SyncHost.Instance.SourceWrapper?.Source?.Name ?? \"还没指定发送源\"), ConsoleColor.Green);\n            }\n            else\n            {\n                if (SyncHost.Instance.Clients.Clients.FirstOrDefault(p => p.ClientName == arg[0]) == null) return false;\n                DefaultConfiguration.Instance.Client = arg[0];\n                SyncHost.Instance.ClientWrapper.ResetClient();\n            }\n            return true;\n        }\n\n        private bool disable(Arguments arg)\n        {\n            if (arg.Count == 0)\n                IO.CurrentIO.WriteColor(LANG_NO_PLUGIN_SELECT, ConsoleColor.Red);\n            else\n                foreach (var item in Sync.SyncHost.Instance.EnumPluings())\n                {\n                    if (item.Name == arg[0])\n                    {\n                        item.OnDisable();\n                        IO.CurrentIO.WriteColor(LANG_PLUGIN_DISABLED + arg[0], ConsoleColor.Red);\n                    }\n                }\n            return true;\n        }\n\n        private bool sourcelogin(Arguments arg)\n        {\n            if (SyncHost.Instance.SourceWrapper.Sendable)\n            {\n                switch (arg.Count)\n                {\n                    case 0:\n                        SyncHost.Instance.SourceWrapper.SendableSource.Login(\"\", \"\");\n                        break;\n\n                    case 1:\n                        SyncHost.Instance.SourceWrapper.SendableSource.Login(arg[0], \"\");\n                        break;\n\n                    case 2:\n                        SyncHost.Instance.SourceWrapper.SendableSource.Login(arg[0], arg[1]);\n                        break;\n\n                    default:\n                        break;\n                }\n            }\n            else\n                IO.CurrentIO.WriteColor(string.Format(LANG_SOURCE_NOT_SUPPORT_SEND, SyncHost.Instance.SourceWrapper.Source?.GetType().Name), ConsoleColor.Red);\n\n            return true;\n        }\n\n        private bool clientmsg(Arguments arg)\n        {\n            if (arg.Count == 0 || (SyncHost.Instance.ClientWrapper.Client.CurrentStatus != SourceStatus.CONNECTED_WORKING))\n            {\n                IO.CurrentIO.Write(LANG_COMMANDS_CHAT_IRC_NOTCONNECT);\n                return true;\n            }\n\n            SyncHost.Instance.Messages.RaiseMessage<ISourceDanmaku>(new DanmakuMessage()\n            {\n                User = \"Console\",\n                Message = string.Join(\" \", arg)\n            });\n            return true;\n        }\n\n        private bool chatuser(Arguments arg)\n        {\n            if (arg.Count < 1 || (SyncHost.Instance.ClientWrapper.Client.CurrentStatus != SourceStatus.CONNECTED_WORKING))\n            {\n                IO.CurrentIO.Write(LANG_COMMANDS_CHAT_IRC_NOTCONNECT);\n            }\n\n            var message = string.Join(\" \",arg.Skip(1));\n\n            SyncHost.Instance.Messages.RaiseMessage<ISourceDanmaku>(new DanmakuMessage()\n            {\n                User = arg[0].Trim(),\n                Message = message\n            });\n            return true;\n        }\n\n        private bool sourcemsg(Arguments arg)\n        {\n            if (SyncHost.Instance.SourceWrapper.Sendable)\n            {\n                if (SyncHost.Instance.SourceWrapper.SendableSource.SendStatus)\n                {\n                    SyncHost.Instance.SourceWrapper.SendableSource.Send(new IRCMessage(string.Empty, string.Join(\"\", arg)));\n                    return true;\n                }\n                else\n                {\n                    IO.CurrentIO.Write(LANG_COMMANDS_DANMAKU_REQUIRE_LOGIN);\n                }\n            }\n            else\n            {\n                IO.CurrentIO.Write(LANG_COMMANDS_DANMAKU_NOT_SUPPORT);\n            }\n            return false;\n        }\n\n        private bool start(Arguments arg)\n        {\n            if (SyncHost.Instance.SourceWrapper.Source == null)\n            {\n                IO.CurrentIO.WriteColor(LANG_COMMANDS_START_NO_SOURCE, ConsoleColor.Red);\n                return true;\n            }\n\n            if (SyncHost.Instance.ClientWrapper.Client == null)\n            {\n                IO.CurrentIO.WriteColor(LANG_COMMANDS_START_NO_CLIENT, ConsoleColor.Red);\n                return true;\n            }\n\n            if (SyncHost.Instance.SourceWrapper.Source.Status == SourceStatus.CONNECTED_WORKING)\n            {\n                IO.CurrentIO.WriteColor(LANG_COMMANDS_START_ALREADY_RUN, ConsoleColor.Red);\n                return true;\n            }\n\n            SyncHost.Instance.ClientWrapper?.Client?.StartWork();\n            SyncHost.Instance.SourceWrapper?.Source?.Connect();\n            return true;\n        }\n\n        private bool stop(Arguments arg)\n        {\n            SyncHost.Instance.ClientWrapper.Client?.StopWork();\n            SyncHost.Instance.SourceWrapper.Source?.Disconnect();\n\n            return true;\n        }\n\n        private bool status(Arguments arg)\n        {\n            IO.CurrentIO.WriteStatus();\n            return true;\n        }\n\n        private bool language(Arguments arg)\n        {\n            if (arg.Count == 0)\n            {\n                CultureInfo info = CultureInfo.GetCultureInfo(I18n.Instance.CurrentLanguage);\n                IO.CurrentIO.WriteColor(string.Format(LANG_COMMANDS_CURRENT_LANG, info.Name, info.NativeName), ConsoleColor.Yellow);\n                return true;\n            }\n            else if (arg.Count == 1)\n            {\n                try\n                {\n                    CultureInfo info = CultureInfo.GetCultureInfo(arg[0]);\n                    DefaultConfiguration.Instance.Language = arg[0];\n                    IO.CurrentIO.WriteColor(string.Format(LANG_COMMANDS_LANG_SWITCHED, arg[0], info.NativeName), ConsoleColor.Green);\n                    return true;\n                }\n                catch (CultureNotFoundException)\n                {\n                    IO.CurrentIO.WriteColor(LANG_COMMANDS_LANG_NOT_FOUND, ConsoleColor.Red);\n                    return false;\n                }\n            }\n            return false;\n        }\n\n        private bool clear(Arguments arg)\n        {\n            IO.CurrentIO.Clear();\n            IO.CurrentIO.WriteWelcome();\n            return true;\n        }\n\n        private bool help(Arguments arg)\n        {\n            IO.CurrentIO.WriteHelp();\n            return true;\n        }\n\n        private static Dictionary<string, Action<Arguments>> MessageManagerCommandMap = new Dictionary<string, Action<Arguments>>\n        {\n            {\"--help\",msgmgr_help},\n            {\"--status\",msgmgr_status},\n            {\"--limit\",msgmgr_limit},\n            {\"--option\",msgmgr_option}\n        };\n\n        private static void msgmgr_help(Arguments arg)\n        {\n            IO.CurrentIO.WriteColor(LANG_COMMANDS_MSGMGR_HELP, ConsoleColor.Yellow);\n        }\n\n        private static void msgmgr_status(Arguments arg)\n        {\n            IO.CurrentIO.WriteColor(String.Format(LANG_COMMANDS_MSGMGR_STATUS, (string)(MessageManager.IsLimit ? LANG_COMMANDS_MSGMGR_LIMIT : LANG_COMMANDS_MSGMGR_FREE), MessageManager.CurrentQueueCount, MessageManager.LimitLevel, MessageManager.RecoverTime, MessageManager.Option.ToString()), ConsoleColor.Yellow);\n        }\n\n        private static void msgmgr_limit(Arguments arg)\n        {\n            if (arg.Count == 2 && Int32.TryParse(arg[1].Trim(), out int value))\n            {\n                MessageManager.LimitLevel = value;\n                IO.CurrentIO.WriteColor(string.Format(LANG_COMMANDS_MSGMGR_LIMIT_SPEED_SET, MessageManager.LimitLevel), ConsoleColor.Yellow);\n            }\n            else\n                IO.CurrentIO.WriteColor(LANG_COMMANDS_ARGUMENT_WRONG, ConsoleColor.Red);\n        }\n\n        private static void msgmgr_option(Arguments arg)\n        {\n            if (arg.Count == 2)\n            {\n                MessageManager.SetOption(arg[1].Trim());\n\n                IO.CurrentIO.WriteColor(string.Format(LANG_COMMANDS_MSGMGR_LIMIT_STYPE_SET, MessageManager.Option.ToString()), ConsoleColor.Yellow);\n            }\n            else\n                IO.CurrentIO.WriteColor(LANG_COMMANDS_ARGUMENT_WRONG, ConsoleColor.Red);\n        }\n\n        private bool msgmgr(Arguments arg)\n        {\n            if (arg.Count == 0)\n                IO.CurrentIO.WriteColor(LANG_COMMANDS_ARGUMENT_WRONG, ConsoleColor.Red);\n            else\n                MessageManagerCommandMap[arg[0]](arg);\n\n            return true;\n        }\n\n        private bool languages(Arguments arg)\n        {\n            if (arg.Count > 0 && arg[0] == \"--all\")\n            {\n                foreach (var item in CultureInfo.GetCultures(CultureTypes.NeutralCultures))\n                {   \n                    IO.CurrentIO.WriteColor(string.Format(\"CultureName: {0:S}\\t{1:S}\", item.Name, item.NativeName), ConsoleColor.Yellow);\n                }\n            }\n            else\n            {\n                foreach (var item in System.IO.Directory.EnumerateDirectories(I18n.Instance.LangFolder))\n                {\n                    string name = item.Substring(item.LastIndexOf('\\\\') + 1);\n                    IO.CurrentIO.WriteColor(string.Format(\"CultureName: {0:S}\\t{1:S}\", name, CultureInfo.GetCultureInfo(name).NativeName), ConsoleColor.Yellow);\n                }\n            }\n            return true;\n        }\n\n        private bool exit(Arguments arg)\n        {\n            stop(arg);\n            SyncHost.Instance.ExitSync();\n            IO.CurrentIO.Write(LANG_COMMANDS_EXIT_DONE);\n            return true;\n        }\n\n        private bool restart(Arguments arg)\n        {\n            Process.Start(Assembly.GetEntryAssembly().Location, \"-f\");\n            Environment.Exit(0);\n            return true;\n        }\n\n        private bool filters(Arguments arg)\n        {\n            foreach (var item in SyncHost.Instance.Filters.GetFiltersEnum())\n            {\n                IO.CurrentIO.WriteColor(\"\", ConsoleColor.Gray, false);\n                IO.CurrentIO.WriteColor(LANG_COMMANDS_FILTERS_ITEM, ConsoleColor.Cyan, false, false);\n                IO.CurrentIO.WriteColor(item.Key.Name.PadRight(22), ConsoleColor.White, false, false);\n                IO.CurrentIO.WriteColor(LANG_COMMANDS_FILTERS_OBJ, ConsoleColor.DarkCyan, false, false);\n                IO.CurrentIO.WriteColor(item.Value.GetType().Name, ConsoleColor.White, true, false);\n            }\n            return true;\n        }\n    }\n}"
  },
  {
    "path": "Sync/Tools/Builtin/InternalPlugin.cs",
    "content": "﻿using Sync.Command;\r\nusing Sync.Plugins;\r\nusing System;\r\n\r\nnamespace Sync.Tools.Builtin\r\n{\r\n    public class InternalPlugin : Plugin\r\n    {\r\n        //private PluginConfigurationManager config;\r\n\r\n        private CommonCommand commonCommand = new CommonCommand();\r\n\r\n        public InternalPlugin() : base(\"InternalPlugin\", \"OsuSync\")\r\n        {\r\n\r\n        }\r\n\r\n        public override void OnEnable()\r\n        {\r\n            EventBus.BindEvent<PluginEvents.InitCommandEvent>(p =>\r\n            {\r\n                Func<string, CommandDelegate, string, bool> addCmd = p.Commands.Dispatch.bind;\r\n                addCmd(\"plugins\", PluginCommand.Instance.Plugins, \"Install & Update Plugins online, type 'plugins' to get help.\");\r\n                commonCommand.BindCommondCommand(p.Commands.Dispatch);\r\n            });\r\n        }\r\n\r\n        internal bool CheckUpdate(string guid) => PluginCommand.Instance.InternalUpdate(guid,true);\r\n    }\r\n}"
  },
  {
    "path": "Sync/Tools/Builtin/PluginCommand.cs",
    "content": "﻿using Sync.Command;\r\nusing Sync.Plugins;\r\nusing System;\r\nusing System.Collections.Generic;\r\nusing System.Diagnostics;\r\nusing System.IO;\r\nusing System.IO.Compression;\r\nusing System.Linq;\r\nusing System.Net;\r\nusing System.Reflection;\r\nusing System.Runtime.Serialization;\r\nusing System.Runtime.Serialization.Json;\r\nusing System.Security.Cryptography;\r\nusing System.Text;\r\nusing static Sync.Tools.DefaultI18n;\r\n\r\nnamespace Sync.Tools.Builtin\r\n{\r\n    internal sealed class PluginCommand\r\n    {\r\n        #region SingleInstance\r\n        private static PluginCommand instance = null;\r\n        internal static PluginCommand Instance => instance ?? (instance = new PluginCommand());\r\n        #endregion\r\n\r\n        #region Updater Decleare\r\n\r\n        [DataContract]\r\n        public class UpdateData\r\n        {\r\n            [DataMember(Order = 0)]\r\n            public int id { get; set; }\r\n\r\n            [DataMember(Order = 1)]\r\n            public string name { get; set; }\r\n\r\n            [DataMember(Order = 2)]\r\n            public string author { get; set; }\r\n\r\n            [DataMember(Order = 3)]\r\n            public string latestHash { get; set; }\r\n\r\n            [DataMember(Order = 4)]\r\n            public string downloadUrl { get; set; }\r\n\r\n            [DataMember(Order = 5)]\r\n            public string description { get; set; }\r\n\r\n            [DataMember(Order = 6)]\r\n            public string guid { get; set; }\r\n\r\n            [DataMember(Order = 7)]\r\n            public string fileName { get; set; }\r\n        }\r\n\r\n        [DataContract]\r\n        public class SyncUpdate\r\n        {\r\n            [DataMember(Order = 0)]\r\n            public string versionHash { get; set; }\r\n\r\n            [DataMember(Order = 1)]\r\n            public string downloadURL { get; set; }\r\n\r\n            [DataMember(Order = 2)]\r\n            public string versionId { get; set; }\r\n        }\r\n\r\n        #endregion Updater Decleare\r\n\r\n        public bool Plugins(Arguments arg)\r\n        {\r\n            if (arg.Count == 0) return Help();\r\n            switch (arg[0])\r\n            {\r\n                case \"search\":\r\n                    return Search(arg[1]);\r\n\r\n                case \"update\":\r\n                    if (arg.Count > 1)\r\n                    {\r\n                        var part_plugin_name = arg[1];\r\n                        return Update(part_plugin_name, arg.Any(a => a == \"--no_ask\"));\r\n                    }\r\n                    else\r\n                        return Update(arg.Any(a => a == \"--no_ask\"));\r\n\r\n                case \"install\":\r\n                    return Install(arg[1]);\r\n\r\n                case \"list\":\r\n                    return List();\r\n\r\n                case \"remove\":\r\n                    return Remove(arg[1]);\r\n\r\n                case \"latest\":\r\n                    return SyncUpdateCheck(true);\r\n\r\n                default:\r\n                    return Help();\r\n            }\r\n        }\r\n\r\n        private bool AskAgreeUpdate(UpdateData data)\r\n        {\r\n            IO.CurrentIO.WriteColor($\"----------------\\nplugin {data.name} have a new version!\\nFile hash:{data.latestHash}\\nDownload? (Y/N):\", ConsoleColor.Green, false);\r\n            var result = IO.CurrentIO.ReadCommand();\r\n            return result.ToLower() == \"y\";\r\n        }\r\n\r\n        public bool ShouldDownloadUpdate(UpdateData update_data, string current_file_path, bool no_ask)\r\n        {\r\n            //todo : version compare\r\n            if (!File.Exists(current_file_path)) { return true; }\r\n\r\n            var current_file_hash = MD5HashFile(current_file_path).ToLower();\r\n\r\n            return (current_file_hash != update_data.latestHash) && AskAgreeUpdate(update_data);\r\n        }\r\n\r\n        internal bool InternalUpdate(string plugin_guid, bool no_ask)\r\n        {\r\n            try\r\n            {\r\n                var result = Serializer<UpdateData>($\"http://sync.mcbaka.com/api/Update/plugin/{plugin_guid}\");\r\n                IO.CurrentIO.Write($\"Fetched update: {result.name} by {result.author} [{plugin_guid}]\");\r\n                var target = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, \"Plugins\", result.fileName);\r\n\r\n                if (ShouldDownloadUpdate(result, target, no_ask))\r\n                {\r\n                    IO.CurrentIO.Write($\"Download: {result.downloadUrl}...\");\r\n\r\n                    if (!DownloadSingleFile(result.downloadUrl, target, result.fileName))\r\n                    {\r\n                        throw new Exception(\"Download update files failed!\");\r\n                    }\r\n                }\r\n                else\r\n                {\r\n                    IO.CurrentIO.Write(string.Format(LANG_VERSION_LATEST_OR_CANEL, result.name));\r\n                }\r\n\r\n                return true;\r\n            }\r\n            catch (Exception e)\r\n            {\r\n                IO.CurrentIO.WriteColor(string.Format(LANG_UPDATE_ERROR, e.TargetSite.Name, e.Message), ConsoleColor.Yellow);\r\n            }\r\n\r\n            return false;\r\n        }\r\n\r\n        internal void InternalUpdate(IEnumerable<Plugin> update_plugins, bool no_ask)\r\n        {\r\n            foreach (var item in update_plugins)\r\n            {\r\n                InternalUpdate(item.getGuid(), no_ask);\r\n            }\r\n\r\n            if (update_plugins.Count() != 0)\r\n                RequireRestart(LANG_UPDATE_DONE);\r\n        }\r\n\r\n        private bool Update(bool no_ask = false)\r\n        {\r\n            IEnumerable<Plugin> plugins = SyncHost.Instance.EnumPluings();\r\n\r\n            InternalUpdate(plugins, no_ask);\r\n\r\n            return true;\r\n        }\r\n\r\n        private bool Update(string part_plugin_name, bool no_ask = false)\r\n        {\r\n            if (string.IsNullOrWhiteSpace(part_plugin_name))\r\n                return Update(no_ask);\r\n\r\n            IEnumerable<Plugin> plugins = from plugin in SyncHost.Instance.EnumPluings()\r\n                                          where plugin.Name.ToLower().Contains(part_plugin_name.ToLower())\r\n                                          select plugin;\r\n\r\n            InternalUpdate(plugins, no_ask);\r\n\r\n            return true;\r\n        }\r\n\r\n        private bool Search(string keyword)\r\n        {\r\n            try\r\n            {\r\n                var result = Serializer<UpdateData[]>($\"http://sync.mcbaka.com/api/Update/search/{keyword}\");\r\n\r\n                foreach (var item in result)\r\n                {\r\n                    IO.CurrentIO.WriteColor(\"Name\", ConsoleColor.Cyan, false, false);\r\n                    IO.CurrentIO.WriteColor(item.name.PadRight(15), ConsoleColor.White, false, false);\r\n                    IO.CurrentIO.WriteColor(\"Author\", ConsoleColor.DarkCyan, false, false);\r\n                    IO.CurrentIO.WriteColor(item.author.PadRight(15), ConsoleColor.White, false, false);\r\n                    IO.CurrentIO.WriteColor(\"Description\", ConsoleColor.DarkCyan, false, false);\r\n                    IO.CurrentIO.WriteColor(item.description, ConsoleColor.White, true, false);\r\n                    IO.CurrentIO.WriteColor(\"GUID \", ConsoleColor.DarkCyan, false, false);\r\n                    IO.CurrentIO.WriteColor(item.guid.PadRight(32), ConsoleColor.White, true, false);\r\n                    IO.CurrentIO.WriteColor(\"===============\", ConsoleColor.White, true, false);\r\n                }\r\n                return true;\r\n            }\r\n            catch (Exception e)\r\n            {\r\n                IO.CurrentIO.Write($\"Error while {e.TargetSite.Name} : {e.Message}\");\r\n            }\r\n            return false;\r\n        }\r\n\r\n        internal bool InstallByKeyword(string keyword, bool requireRestart = true)\r\n        {\r\n            if (Serializer<UpdateData[]>($\"http://sync.mcbaka.com/api/Update/search/{keyword}\") is UpdateData[] datas)\r\n            {\r\n                if (datas.Length == 0 || InternalUpdate(datas[0].guid, true))\r\n                {\r\n                    if (requireRestart) RequireRestart(LANG_INSTALL_DONE);\r\n                    return true;\r\n                }\r\n                else return false;\r\n            }\r\n            return false;\r\n        }\r\n\r\n        private bool Install(string guid)\r\n        {\r\n            if (InternalUpdate(guid, true))\r\n            {\r\n                RequireRestart(LANG_INSTALL_DONE);\r\n                return true;\r\n            }\r\n            else\r\n            {\r\n                return InstallByKeyword(guid);\r\n            }\r\n        }\r\n\r\n        private bool Remove(string name)\r\n        {\r\n            var type = SyncHost.Instance.EnumPluings().FirstOrDefault(p => p.Name.ToLower().Contains(name.ToLower()));\r\n            if (type == null)\r\n            {\r\n                IO.CurrentIO.WriteColor(string.Format(LANG_PLUGIN_NOT_FOUND, name), ConsoleColor.Red);\r\n                return false;\r\n            }\r\n            else\r\n            {\r\n                var result = Serializer<UpdateData[]>($\"http://sync.mcbaka.com/api/Update/search/{name}\")?[0];\r\n                var target = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, \"Plugins\", Path.GetFileName(result.fileName));\r\n                if (File.Exists(target)) File.Delete(target);\r\n\r\n                RequireRestart(LANG_REMOVE_DONE);\r\n                return true;\r\n            }\r\n        }\r\n\r\n        private bool List()\r\n        {\r\n            var list = SyncHost.Instance.EnumPluings();\r\n            foreach (var item in list)\r\n            {\r\n                IO.CurrentIO.WriteColor(\"Name\", ConsoleColor.Cyan, false, false);\r\n                IO.CurrentIO.WriteColor(item.Name.PadRight(25), ConsoleColor.White, false, false);\r\n                IO.CurrentIO.WriteColor(\"Author\", ConsoleColor.DarkCyan, false, false);\r\n                IO.CurrentIO.WriteColor(item.Author.PadRight(20), ConsoleColor.White, false, false);\r\n                var info = item.GetType().GetCustomAttribute<SyncPluginID>();\r\n                IO.CurrentIO.WriteColor(\"Support Update:\", ConsoleColor.DarkCyan, false, false);\r\n                if (info != null)\r\n                {\r\n                    IO.CurrentIO.WriteColor(\"Yes\".PadRight(15), ConsoleColor.White, false, false);\r\n                    IO.CurrentIO.WriteColor(\"Ver:\", ConsoleColor.DarkCyan, false, false);\r\n                    IO.CurrentIO.WriteColor(info.Version.PadRight(15), ConsoleColor.White, true, false);\r\n                }\r\n                else\r\n                {\r\n                    IO.CurrentIO.WriteColor(\"No\", ConsoleColor.White, true, false);\r\n                }\r\n            }\r\n            return true;\r\n        }\r\n\r\n        internal bool SyncUpdateCheck(bool download = false)\r\n        {\r\n            try\r\n            {\r\n                IO.CurrentIO.WriteColor(\"Fetch Sync update..\", ConsoleColor.Cyan);\r\n                var result = Serializer<SyncUpdate>($\"http://sync.mcbaka.com/api/Update/latest\");\r\n\r\n                if (!File.Exists(Updater.CurrentFullSourceEXEPath) || MD5HashFile(Updater.CurrentFullSourceEXEPath) != result.versionHash)\r\n                {\r\n                    if (download)\r\n                    {\r\n                        IO.CurrentIO.Write($\"Download: {result.downloadURL}...\");\r\n                        DownloadSingleFile(result.downloadURL, Updater.CurrentFullUpdateEXEPath, \"Sync\");\r\n                        RequireRestart(\"Update downloaded. Restart to apply effect\");\r\n                    }\r\n                    else\r\n                        IO.CurrentIO.WriteColor($\"There is a new version Sync! please visit https://github.com/OsuSync/Sync/releases/latest \" +\r\n                            $\", you can type \\\"plugins latest\\\" in Sync for automatically updating self.\", ConsoleColor.Cyan);\r\n                }\r\n                else\r\n                    IO.CurrentIO.WriteColor(\"Sync update check done.Enjoy~\", ConsoleColor.Cyan);\r\n            }\r\n            catch (Exception e)\r\n            {\r\n                IO.CurrentIO.WriteColor(\"Fetch Sync update info failed,please check your network if it can able to connect http://sync.mcbaka.com/\", ConsoleColor.Red);\r\n                return false;\r\n            }\r\n            return true;\r\n        }\r\n\r\n        private bool Help()\r\n        {\r\n            IO.CurrentIO.Write(\"Help for 'plugins' command:\");\r\n            IO.CurrentIO.WriteHelp(\"install\", \"install [guid/name] Install plugin\");\r\n            IO.CurrentIO.WriteHelp(\"remove\", \"remove [name] Remove plugin by name\");\r\n            IO.CurrentIO.WriteHelp(\"search\", \"search [keyword] Search plugins\");\r\n            IO.CurrentIO.WriteHelp(\"update\", \"Check update for Sync and Plugins\");\r\n            IO.CurrentIO.WriteHelp(\"list\", \"List current installed plugins\");\r\n            IO.CurrentIO.WriteHelp(\"latest\", \"Check latest Sync update\");\r\n            return true;\r\n        }\r\n\r\n        private void RequireRestart(string msg)\r\n        {\r\n            IO.CurrentIO.WriteColor($\"{msg}? (Y/N):\", ConsoleColor.Green, false);\r\n            var result = IO.CurrentIO.ReadCommand();\r\n            if (result.ToLower().StartsWith(\"y\")) SyncHost.Instance.RestartSync();\r\n        }\r\n\r\n        private T Serializer<T>(string url)\r\n        {\r\n            WebClient web;\r\n            web = new WebClient();\r\n            Random rd = new Random();\r\n            Stream data = web.OpenRead(url + \"?rd=\" + rd.Next());\r\n            DataContractJsonSerializer serializer = new DataContractJsonSerializer(typeof(T));\r\n            return (T)serializer.ReadObject(data);\r\n        }\r\n\r\n        private string MD5HashFile(string filePath)\r\n        {\r\n            FileStream fs = new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.Read);\r\n            MD5CryptoServiceProvider md5 = new MD5CryptoServiceProvider();\r\n            md5.ComputeHash(fs);\r\n            byte[] b = md5.Hash;\r\n\r\n            fs.Dispose();\r\n            md5.Clear();\r\n\r\n            StringBuilder sb = new StringBuilder(32);\r\n            for (int i = 0; i < b.Length; i++)\r\n                sb.Append(b[i].ToString(\"X2\"));\r\n            return sb.ToString();\r\n        }\r\n\r\n        private bool DownloadSingleFile(string dlUrl, string path, string name)\r\n        {\r\n            try\r\n            {\r\n                //打开HTTP连接，获得文件长度\r\n                IO.CurrentIO.WriteColor($\"Download {name} from {dlUrl}\", ConsoleColor.Magenta);\r\n                ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12 | SecurityProtocolType.Ssl3;\r\n                HttpWebRequest Myrq = (HttpWebRequest)WebRequest.Create(dlUrl);\r\n                HttpWebResponse myrp = (HttpWebResponse)Myrq.GetResponse();\r\n                long totalBytes = myrp.ContentLength;\r\n                string convertdTotal = (totalBytes / 1024).ToString(\"0.00\");\r\n                IO.CurrentIO.WriteHelp(name, $\"Total: {convertdTotal} KB\");\r\n                //判断是否存在已经下载的文件，如果存在，且长度相等，则直接解压\r\n\r\n                if (File.Exists(path + \"_\")) File.Delete(path + \"_\");       //如果存在目标缓存文件，就删掉它\r\n\r\n                Stream st = myrp.GetResponseStream();                       //获得http流\r\n                Stream so = new FileStream(path + \"_\", FileMode.Create);    //创建文件流\r\n\r\n                long totalDownloadedByte = 0;                               //已经下载的字节数量\r\n                long updateDownloadByte = 0;                                //已经计算下载速度的字节位置\r\n                long downloadspeed = 0;                                     //当前下载速度 byte\r\n                Stopwatch time = new Stopwatch();                           //下载速度计时器\r\n                time.Reset();\r\n                time.Start();\r\n                byte[] buffer = new byte[1024];                             //缓冲区\r\n                int osize = st.Read(buffer, 0, buffer.Length);              //向缓冲区填入数据\r\n\r\n                while (osize > 0)                                           //判断还剩没剩数据\r\n                {\r\n                    so.Write(buffer, 0, osize);                             //写入文件流\r\n\r\n                    totalDownloadedByte += osize;      //更新当前下载进度\r\n                    osize = st.Read(buffer, 0, buffer.Length);              //获得下一个缓冲区\r\n\r\n                    if (time.ElapsedMilliseconds > 1000)                     //数据统计大于1000ms，则更新下载速度\r\n                    {\r\n                        downloadspeed = totalDownloadedByte - updateDownloadByte;                   //这段时间已经下载的数据量\r\n                        time.Restart();                                     //计时器重新开始计时\r\n                        updateDownloadByte = totalDownloadedByte;           //更新已经计算速度的数据位置\r\n                        IO.CurrentIO.WriteHelp($\"{(downloadspeed / 1024).ToString(\"0.0\")}KB/s\", $\"{(totalDownloadedByte / 1024).ToString(\"0.0\")}/{convertdTotal} KB\");//UI更新\r\n                    }\r\n                }\r\n\r\n                so.Close();\r\n                st.Close();\r\n\r\n                if (File.Exists(path))\r\n                {\r\n                    File.Delete(path);\r\n                }\r\n\r\n                File.Copy(path + \"_\", path);\r\n                File.Delete(path + \"_\");\r\n\r\n                if (dlUrl.EndsWith(\"zip\"))\r\n                {\r\n                    var zip = Path.Combine(Path.GetDirectoryName(path), Path.GetFileNameWithoutExtension(path) + \".zip\");\r\n                    if (File.Exists(zip)) File.Delete(zip);\r\n                    File.Move(path, zip);\r\n                    using (var archive = ZipFile.Open(zip, ZipArchiveMode.Update))\r\n                    {\r\n                        foreach (ZipArchiveEntry entry in archive.Entries)\r\n                        {\r\n                            if (entry.Length == 0) continue;\r\n                            IO.CurrentIO.WriteHelp(entry.FullName, entry.Length.ToString());\r\n                            try\r\n                            {\r\n                                string fileFullName = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, entry.FullName);\r\n                                string directoryName = Path.GetDirectoryName(fileFullName);\r\n                                if (!Directory.Exists(directoryName))\r\n                                    Directory.CreateDirectory(directoryName);\r\n                                entry.ExtractToFile(fileFullName, true);\r\n                            }\r\n                            catch { }\r\n                        }\r\n                    }\r\n\r\n                    File.Delete(zip);\r\n                }\r\n\r\n                IO.CurrentIO.WriteColor($\"[{name}] Done.\", ConsoleColor.Green);\r\n                return true;\r\n            }\r\n            catch (Exception e)\r\n            {\r\n                IO.CurrentIO.WriteColor($\"Error while {e.TargetSite.Name} : {e.Message}\", ConsoleColor.Red);\r\n                return false;\r\n            }\r\n        }\r\n    }\r\n}\r\n"
  },
  {
    "path": "Sync/Tools/CommandParser.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace Sync.Tools\n{\n    internal class CommandParser<T>\n    {\n        //storage the commands parse to the command exector\n        string[] args;\n        List<Action<T>> parsedArgumentActions = new List<Action<T>>();\n        List<Action<T>> parsedExecuteActions = new List<Action<T>>();\n        Dictionary<string, Func<string, Action<T>>> parseArgumentBinder;\n        Dictionary<string, Func<string, Action<T>>> parseActionBinder;\n        int parseSharedIndex = 0;\n        internal CommandParser(string[] args) : this(args, new Dictionary<string, Func<string, Action<T>>>(), new Dictionary<string, Func<string, Action<T>>>())\n        {\n        }\n        internal CommandParser(string[] args, Dictionary<string, Func<string, Action<T>>> arguments, Dictionary<string, Func<string, Action<T>>> actions)\n        {\n            this.args = args;\n            this.parseArgumentBinder = arguments;\n            this.parseActionBinder = actions;\n            ParseCommands();\n        }\n        //Parse commands form args to Actions\n        public void ParseCommands()\n        {\n            try\n            {\n                foreach (var item in args)\n                {\n                    ParseStringBlock($\"{item} \");\n                }\n            }\n            catch (Exception)\n            {\n                Console.WriteLine(\"Wrong argument\");\n            }\n        }\n        private void ParseStringBlock(string value)\n        {\n            if (value.StartsWith(\"--\"))\r\n            {\r\n                ParseArgumentFullArgs(value.TrimStart().TrimEnd());\r\n            }\n            else if (value.StartsWith(\"-\"))\n            {\n                ParseArgumentArgs(value.Substring(1));\n            }\n            else\n            {\n                ParseExecuteArgs(value);\n            }\n        }\n        private void ParseArgumentFullArgs(string value)\r\n        {\r\n            parsedArgumentActions.Add(parseArgumentBinder[value](value));\r\n        }\n        private void ParseArgumentArgs(string value)\n        {\n            for (parseSharedIndex = 0; parseSharedIndex < value.Length - 1; parseSharedIndex++)\n            {\n                parsedArgumentActions.Add(parseArgumentBinder[value.Substring(parseSharedIndex, 1)](value));\n\n            }\n        }\n        private void ParseExecuteArgs(string value)\n        {\n            if (value.Length > 0)\n            {\n                parsedExecuteActions.Add(parseActionBinder[value.Substring(0, 1)](value));\n            }\n        }\n        #region ParseTools\n        private string ReadLiteralString(string value)\n        {\n            string result = string.Empty;\n            //is quote \", read until next quote \"\n            char current = value[++parseSharedIndex];\n            while (current != '\"')\n            {\n                result += current;\n                current = value[++parseSharedIndex];\n            }\n            return result;\n        }\n\n        private string ReadNumberToString(string value)\n        {\n            string result = string.Empty;\n            char current = value[parseSharedIndex++];\n            while (char.IsDigit(current))\n            {\n                result += current;\n                current = value[parseSharedIndex++];\n            }\n            parseSharedIndex -= 2;\n            return result;\n        }\n        private string GetSubArgument(string value)\n        {\n            char peek = value[++parseSharedIndex];\n            if (peek == '\"') return ReadLiteralString(value);\n            if (char.IsDigit(peek)) return ReadNumberToString(value);\n            return string.Empty;\n        }\n        #endregion\n\n        public void ExecuteActionOn(T instance)\n        {\n            foreach (var item in parsedArgumentActions)\n            {\n                item.Invoke(instance);\n            }\n            foreach (var item in parsedExecuteActions)\n            {\n                item.Invoke(instance);\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "Sync/Tools/Configuration.cs",
    "content": "﻿using Sync.MessageFilter;\nusing Sync.Tools.ConfigurationAttribute;\n\nnamespace Sync.Tools\n{\n    /// <summary>\n    /// Default plugin confiuration\n    /// </summary>\n    public class DefaultConfiguration : IConfigurable\n    {\n        public const string DEFAULT_LANGUAGE = \"LocalSettings\";\n\n        [ClientList(NoCheck = true)]\n        public ConfigurationElement Client { get; set; } = \"\";\n\n        [SourceList(NoCheck = true)]\n        public ConfigurationElement Source { get; set; } = \"\";\n\n        public ConfigurationElement Language { get; set; } = \"\";\n\n        [Path(IsDirectory = true)]\n        public ConfigurationElement LogDirectory { get; set; } = @\"Logs\\\";\n\n        [String]\n        public ConfigurationElement LogFilename { get; set; } = @\"Log-{0}.txt\";\n\n        [Bool]\n        public ConfigurationElement EnableViewersChangedNotify { get; set; } = \"False\";\n\n        [Bool]\n        public ConfigurationElement EnableGiftChangedNotify { get; set; } = \"False\";\n\n        [Bool]\n        public ConfigurationElement CheckUpdateOnStartup { get; set; } = \"True\";\n\n        [List(ValueList = new[] { \"Auto\", \"ForceAll\", \"OnlySendCommand\", \"DisableAll\" }, IgnoreCase = true)]\n        public ConfigurationElement MessageManagerDefaultOption { get; set; } = \"Auto\";\n\n        public void onConfigurationLoad()\n        {\n            MessageManager.SetOption(MessageManagerDefaultOption);\n        }\n\n        public void onConfigurationReload()\n        {\n            MessageManager.SetOption(MessageManagerDefaultOption);\n        }\n\n        public void onConfigurationSave()\n        {\n            MessageManagerDefaultOption = MessageManager.Option.ToString();\n        }\n\n        public static readonly DefaultConfiguration Instance = new DefaultConfiguration();\n        private static readonly PluginConfigurationManager config = new PluginConfigurationManager(\"Sync\");\n        static DefaultConfiguration()\n        {\n            config.AddItem(Instance);\n        }\n\n    }\n}"
  },
  {
    "path": "Sync/Tools/ConfigurationAttribute/BaseConfigurationAttribute.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Globalization;\nusing System.IO;\nusing System.Linq;\nusing System.Reflection;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace Sync.Tools.ConfigurationAttribute\n{\n    [System.AttributeUsage(System.AttributeTargets.Property, AllowMultiple = false)]\n    public abstract class BaseConfigurationAttribute : Attribute\n    {\n        public bool RequireRestart { get; set; } = false;\n\n        public bool Hide { get; set; }\n        public bool NoCheck { get; set; }\n\n        public virtual string CheckFailedFormatMessage { get; set; } = \"Parse error:{0}\";\n        public abstract bool Check(string value);\n        public void CheckFailedNotify(object obj) => IO.CurrentIO.WriteColor($\"[Config]{string.Format(CheckFailedFormatMessage, obj.ToString())}\", ConsoleColor.Red);\n    }\n}\n"
  },
  {
    "path": "Sync/Tools/ConfigurationAttribute/BoolAttribute.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace Sync.Tools.ConfigurationAttribute\n{\n    public class BoolAttribute : BaseConfigurationAttribute\n    {\n        public override bool Check(string value) => bool.TryParse(value,out _);\n    }\n}\n"
  },
  {
    "path": "Sync/Tools/ConfigurationAttribute/ClientAndSourceAttribute.cs",
    "content": "﻿using Sync.Client;\nusing Sync.Plugins;\nusing Sync.Tools.ConfigurationAttribute;\nusing System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace Sync.Tools.ConfigurationAttribute\n{\n    class ClientListAttribute : ListAttribute\n    {\n        public override string[] ValueList => ClientManager.Instance.Clients.Select(c=>c.ClientName).ToArray();\n    }\n\n    class SourceListAttribute : ListAttribute\n    {\n        public override string[] ValueList => SyncHost.Instance.Sources.SourceList.Select(s => s.Name).ToArray();\n    }\n}\n"
  },
  {
    "path": "Sync/Tools/ConfigurationAttribute/ColorAttribute.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Globalization;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace Sync.Tools.ConfigurationAttribute\n{\n    public class ColorAttribute : BaseConfigurationAttribute\n    {\n        //#RRGGBBAA\n        public override bool Check(string rgba)\n        {\n            return rgba.Length == 9\n                && rgba[0] == '#'\n                && byte.TryParse(rgba.Substring(1, 2), NumberStyles.HexNumber, null, out var _)\n                && byte.TryParse(rgba.Substring(3, 2), NumberStyles.HexNumber, null, out var _)\n                && byte.TryParse(rgba.Substring(5, 2), NumberStyles.HexNumber, null, out var _)\n                && byte.TryParse(rgba.Substring(7, 2), NumberStyles.HexNumber, null, out var _);\n        }\n    }\n}\n"
  },
  {
    "path": "Sync/Tools/ConfigurationAttribute/ConfigurationHolderAttribute.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace Sync.Tools.ConfigurationAttribute\n{\n    [System.AttributeUsage(System.AttributeTargets.Class, AllowMultiple = false)]\n    public class ConfigurationHolderAttribute:Attribute\n    {\n        public bool Hide { get; set; } = false;\n        public bool NoCheck { get; set; } = false;\n    }\n}\n"
  },
  {
    "path": "Sync/Tools/ConfigurationAttribute/FloatAttribute.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace Sync.Tools.ConfigurationAttribute\n{\n    public class FloatAttribute : BaseConfigurationAttribute\n    {\n        public float MinValue { get; set; } = float.MinValue;\n        public float MaxValue { get; set; } = float.MaxValue;\n        public float Step { get; set; } = 1;\n\n        public override bool Check(string o)\n        {\n            if (!float.TryParse(o, out float v))\n                return false;\n            return (MinValue <= v && v <= MaxValue);\n        }\n    }\n}\n"
  },
  {
    "path": "Sync/Tools/ConfigurationAttribute/FontAttribute.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace Sync.Tools.ConfigurationAttribute\n{\n    public class FontAttribute : BaseConfigurationAttribute\n    {\n        public override bool Check(string value)\n        {\n            return true;\n        }\n    }\n}\n"
  },
  {
    "path": "Sync/Tools/ConfigurationAttribute/GuiLanguageElement.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace Sync.Tools.ConfigurationAttribute\n{\n    public struct GuiLanguageElement\n    {\n        private LanguageElement element;\n\n        public GuiLanguageElement(string defaultVal)\n        {\n            element = new LanguageElement(defaultVal);\n        }\n\n        public static implicit operator GuiLanguageElement(string val)\n        {\n            return new GuiLanguageElement(val);\n        }\n\n        public static implicit operator string(GuiLanguageElement element)\n        {\n            return element.ToString();\n        }\n\n        public override string ToString()\n        {\n            return element.ToString();\n        }\n    }\n}\n"
  },
  {
    "path": "Sync/Tools/ConfigurationAttribute/IntegerAttribute.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace Sync.Tools.ConfigurationAttribute\n{\n    public class IntegerAttribute : BaseConfigurationAttribute\n    {\n        public int MinValue { get; set; } = int.MinValue;\n        public int MaxValue { get; set; } = int.MaxValue;\n\n        public override bool Check(string i)\n        {\n            if (!int.TryParse(i, out int v))\n                return false;\n            return (MinValue <= v && v <= MaxValue);\n        }\n    }\n}\n"
  },
  {
    "path": "Sync/Tools/ConfigurationAttribute/ListAttribute.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace Sync.Tools.ConfigurationAttribute\n{\n    public class ListAttribute : BaseConfigurationAttribute\n    {\n        public virtual string[] ValueList { get; set; } = new string[] { };\n\n        public bool IgnoreCase { get; set; } = false;\n\n        public bool AllowMultiSelect { get; set; } = false;\n        public char SplitSeparator { get; set; } = ',';\n\n        public override bool Check(string val)\n        {\n            var m_val = IgnoreCase ? val.ToLower() : val;\n\n            if ((ValueList.Length != 0))\n            {\n                if (AllowMultiSelect)\n                {\n                    foreach (var str in m_val.Split(new[] { SplitSeparator }, StringSplitOptions.RemoveEmptyEntries))\n                    {\n                        if (!ContainValue(str))\n                        {   \n                            CheckFailedNotify(val);\n                            return false;\n                        }\n                    }\n                }\n                else\n                {\n                    if (!ContainValue(m_val))\n                    {\n                        CheckFailedNotify(val);\n                        return false;\n                    }\n                }\n            }\n\n            return true;\n        }\n\n        private bool ContainValue(string content)\n        {\n            return ValueList.Where((str) => (IgnoreCase ? str.ToLower() : str) == content).Count() != 0;\n        }\n    }\n}\n"
  },
  {
    "path": "Sync/Tools/ConfigurationAttribute/PathAttribute.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.IO;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace Sync.Tools.ConfigurationAttribute\n{\n    public class PathAttribute : BaseConfigurationAttribute\n    {\n        /// <summary>\n        /// 是否钦定这路径是否必须存在,通常用于读取配置文件\n        /// </summary>\n        public bool RequireExist { get; set; } = false;\n\n        public bool IsDirectory { get; set; } = true;\n\n        public override bool Check(string file_path)\n        {\n            if (RequireExist && (!(IsDirectory ? File.Exists(file_path) : Directory.Exists(file_path))))\n            {\n                CheckFailedNotify(file_path);\n                return false;\n            }\n\n            return true;\n        }\n    }\n}\n"
  },
  {
    "path": "Sync/Tools/ConfigurationAttribute/StringAttribute.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace Sync.Tools.ConfigurationAttribute\n{\n    public class StringAttribute : BaseConfigurationAttribute\n    {\n        public override bool Check(string value) => true;\n    }\n}\n"
  },
  {
    "path": "Sync/Tools/ConfigurationIO.cs",
    "content": "﻿using System;\nusing System.IO;\nusing System.Runtime.InteropServices;\nusing System.Text;\nusing System.Threading;\nusing System.Threading.Tasks;\n\nnamespace Sync.Tools\n{\n    /// <summary>\n    /// INI File reader\n    /// </summary>\n    static class ConfigurationIO\n    {\n        /// <summary>\n        /// InI key\n        /// </summary>\n        public enum DefaultConfig\n        {\n            EnableViewersChangedNotify,\n            EnableGiftChangedNotify,\n            Client,\n            Source,\n            Language,\n            LoggerFile,\n        }\n\n        private static FileSystemWatcher watcher;\n\n        static ConfigurationIO()\n        {\n            watcher = new FileSystemWatcher(AppDomain.CurrentDomain.BaseDirectory);\n            watcher.NotifyFilter = NotifyFilters.LastWrite | NotifyFilters.Size;\n            watcher.EnableRaisingEvents = true;\n            watcher.Changed += (s, e) => {\n                if (PluginConfigurationManager.InSaving) return;\n                if (!e.Name.StartsWith(\"config.ini\")) return;\n\n                watcher.EnableRaisingEvents = false;\n                Task.Run(()=> {\n                    Thread.Sleep(500);\n                    watcher.EnableRaisingEvents = true;\n                });\n\n                Thread.Sleep(100);\n\n                foreach (var item in PluginConfigurationManager.ConfigurationSet)\n                {\n                    item.ReloadAll();\n                }\n                Plugins.PluginEvents.Instance.RaiseEventAsync(new Plugins.PluginEvents.ConfigurationChange());\n            };\n        }\n\n        [DllImport(\"kernel32\")]\n        private static extern int GetPrivateProfileString(string section, string key, string defVal, StringBuilder retVal, int size, string filePath);\n\n        [DllImport(\"kernel32\")]\n        private static extern bool WritePrivateProfileString(string section, string key, string val, string filePath);\n        /// <summary>\n        /// Config path\n        /// </summary>\n        public readonly static string ConfigFile = AppDomain.CurrentDomain.BaseDirectory + \"config.ini\";\n\n        /// <summary>\n        /// The ini read buffer(thread safe)\n        /// </summary>\n        private readonly static ThreadLocal<StringBuilder> iniReadBuffer = new ThreadLocal<StringBuilder>(()=>new StringBuilder(65535));\n\n        /// <summary>\n        /// Read value\n        /// </summary>\n        /// <param name=\"key\">Key</param>\n        /// <param name=\"column\">Section</param>\n        /// <returns>Value</returns>\n        internal static string IniReadValue(string FilePath, string key, string column = \"config\")\n        {\n            StringBuilder temp = iniReadBuffer.Value;\n            GetPrivateProfileString(column, key, \"\", temp, 65535, FilePath);\n            return temp.ToString();\n        }\n\n        internal static bool IniWriteValue(string FilePath, string key, string value, string column = \"config\")\n        {\n            return WritePrivateProfileString(column, key, value, FilePath);\n        }\n        /// <summary>\n        /// Read value\n        /// </summary>\n        /// <param name=\"key\">Key</param>\n        /// <param name=\"column\">Section</param>\n        /// <returns>Value</returns>\n        public static string Read(string key, string column = \"config\")\n        {\n            return IniReadValue(ConfigFile, key, column);\n        }\n        /// <summary>\n        /// Read via enum\n        /// </summary>\n        /// <param name=\"key\">Key</param>\n        /// <returns>Value</returns>\n        public static string ReadConfig(DefaultConfig key)\n        {\n            return IniReadValue(ConfigFile, Enum.GetName(typeof(DefaultConfig), key));\n        }\n        /// <summary>\n        /// Write <see cref=\"value\"/> to <see cref=\"key\"/>\n        /// </summary>\n        /// <param name=\"key\">Key</param>\n        /// <param name=\"value\">Value</param>\n        /// <param name=\"column\">Section</param>\n        /// <returns></returns>\n        public static bool Write(string key, string value, string column = \"config\")\n        {\n            return IniWriteValue(ConfigFile, key, value, column);\n        }\n        /// <summary>\n        /// Write value via enum\n        /// </summary>\n        /// <param name=\"key\">Key</param>\n        /// <param name=\"value\">value</param>\n        /// <returns></returns>\n        public static bool WriteConfig(DefaultConfig key, string value)\n        {\n            return Write(Enum.GetName(typeof(DefaultConfig), key), value);\n        }\n    }\n}\n"
  },
  {
    "path": "Sync/Tools/I18n.cs",
    "content": "﻿using Sync.Tools.ConfigurationAttribute;\nusing System;\nusing System.Collections.Generic;\nusing System.IO;\nusing System.Reflection;\n\nnamespace Sync.Tools\n{\n    public class DefaultI18n : I18nProvider\n    {\n        public static LanguageElement LANG_Loading = \"读取中....\";\n        public static LanguageElement LANG_Plugins = \"已载入 {0:D} 个 插件\";\n        public static LanguageElement LANG_Sources = \"已载入 {0:D} 个 直播源\";\n        public static LanguageElement LANG_Client = \"已载入 {0:D} 个 Client\";\n        public static LanguageElement LANG_Error = \"不能初始化连接器，请确认是否已经安装直播源.\";\n        public static LanguageElement LANG_Commands = \"已载入 {0:D} 个 命令\";\n        public static LanguageElement LANG_Filters = \"已载入 {0:D} 个 过滤器\";\n        public static LanguageElement LANG_Ready = \"准备就绪。\";\n\n        public static LanguageElement LANG_RqueireLogin = \"请登录到RnW账号\";\n        public static LanguageElement LANG_AccountName = \"用户名:\";\n        public static LanguageElement LANG_AccountPw = \"密码:\";\n        public static LanguageElement LANG_AccountSave = \"账户保存成功! 将开始连接到服务器\";\n\n        public static LanguageElement LANG_Start = \"开始工作....\";\n        public static LanguageElement LANG_Stopping = \"停止工作...\";\n        public static LanguageElement LANG_Restarting = \"重新开始工作...\";\n\n        public static LanguageElement LANG_LoadingPlugin = \"载入 {0:S} 中...\";\n        public static LanguageElement LANG_LoadPluginErr = \"不能载入 {0:S} ({1:S})\";\n        public static LanguageElement LANG_NotPluginErr = \"插件{0:S} 非osuSync插件 ({1:S})\";\n\n        public static LanguageElement LANG_NotConfig = \"请配置 'config.ini' 后再开始进行同步操作。\";\n        public static LanguageElement LANG_NoSource = \"无法找到任何直播源！请安装一个直播源。\";\n        public static LanguageElement LANG_MissSource = \"找不到默认匹配的直播源，直接使用第一个。\";\n        public static LanguageElement LANG_SetSource = \"设置 {0:S} 为直播弹幕源\";\n        public static LanguageElement LANG_SupportSend = \"提示:当前弹幕源支持游戏内发送到弹幕源的功能，请输入login [用户名] [密码] 来登录!(用户名、密码二者可选输入)\";\n        public static LanguageElement LANG_CertLength = \"Certification长度: {0:D}\";\n        public static LanguageElement LANG_CertExist = \"提示：当前已有登录Certification记录，如需覆盖，请输入login [用户名] [密码]进行覆盖！（用户名密码可选输入）\";\n        public static LanguageElement LANG_SendNotReady = \"当前Client未标志弹幕发送可用，请尝试使用Login登录\";\n\n        public static LanguageElement LANG_UnknowCommand = \"未知命令！ 请输入help查看命令列表。\";\n        public static LanguageElement LANG_CommandFail = \"命令执行失败！ 请输入help查看命令列表。\";\n\n        public static LanguageElement LANG_ConfigFile = \"配置文件\";\n\n        public static LanguageElement LANG_UserCount = \"用户总数变更: {0:D}\";\n        public static LanguageElement LANG_UserCount_Change = \"直播间围观人数{0:S}到{1:D}人\";\n        public static LanguageElement LANG_UserCount_Change_Increase = \"增加\";\n        public static LanguageElement LANG_UserCount_Change_Decrease = \"减少\";\n\n        public static LanguageElement LANG_Source_Disconnecting = \"正在断开弹幕源服务器的连接....\";\n        public static LanguageElement LANG_Source_Disconnected = \"服务器连接被断开，3秒后重连！\";\n        public static LanguageElement LANG_Source_Disconnected_Succ = \"源服务器断开连接成功！\";\n        public static LanguageElement LANG_Source_Connect = \"正在连接弹幕源服务器....\";\n        public static LanguageElement LANG_Source_Connected_Succ = \"源服务器连接成功！\";\n\n        public static LanguageElement LANG_Current_Online = \"当前在线人数: {0:D}\";\n        public static LanguageElement LANG_Gift_Sent = \"我送给你{O:D}份{1:S}!\";\n\n        public static LanguageElement LANG_Config = \"配置文件: \";\n        public static LanguageElement LANG_Config_Status_OK = \"OK, 房间ID:{0}\";\n        public static LanguageElement LANG_Config_Status_Fail = \"尚未配置成功\";\n\n        public static LanguageElement LANG_Source = \"源{0:S}: \";\n        public static LanguageElement LANG_IRC = \"Client:\";\n        public static LanguageElement LANG_Danmaku = \"弹幕发送:\";\n        public static LanguageElement LANG_Status_Connected = \"已连接\";\n        public static LanguageElement LANG_Status_NotConenct = \"未连接\";\n\n        public static LanguageElement LANG_Loading_Config = @\"正在读取配置文件....\\n\";\n\n        public static LanguageElement LANG_Welcome = \"欢迎使用 osu直播弹幕同步工具 ver {0:S} \";\n        public static LanguageElement LANG_Help = @\"输入 'help' 获得帮助列表\\n\\n\";\n        public static LanguageElement LANG_Command = \"命令\";\n        public static LanguageElement LANG_Command_Description = \"描述\";\n\n        public static LanguageElement LANG_MsgMgr_Limit = \"当前消息管理器 开始 管制，只有?send命令的内容才会发送到irc频道\";\n        public static LanguageElement LANG_MsgMgr_Free = \"当前消息管理器 解除 管制,内容可以直接发送到irc频道\";\n        public static LanguageElement LANG_Plugin_Cycle_Reference = \"发现插件之间的循环引用关系，插件 {0:S} 将不会按照开发者指定的依赖关系进行加载\";\n\n\n        //from default plugin\n        public static LanguageElement LANG_COMMANDS_LOGIN = \"login <user> [pass] 登录到目标弹幕网站，启动弹幕发送功能\";\n        public static LanguageElement LANG_COMMANDS_EXIT = \"退出软件\";\n        public static LanguageElement LANG_COMMANDS_CLEAR = \"清空屏幕\";\n        public static LanguageElement LANG_COMMANDS_STATUS = \"获得当前连接状态属性\";\n        public static LanguageElement LANG_COMMANDS_STOP = \"停止当前连接\";\n        public static LanguageElement LANG_COMMANDS_START = \"开始同步\";\n        public static LanguageElement LANG_COMMANDS_HELP = \"打印帮助信息\";\n        public static LanguageElement LANG_COMMANDS_SOURCEMSG = \"danmaku <message> 发送弹幕测试\";\n        public static LanguageElement LANG_COMMANDS_CLIENTMSG = \"chat <message> 发送IRC信息测试\";\n        public static LanguageElement LANG_COMMANDS_CLIENTUSERMSG = \"chatuser <username> <message> 按照username名字发送IRC信息测试\";\n        public static LanguageElement LANG_COMMANDS_EXIT_DONE = \"退出操作已完成，如果窗口还未关闭，您可以强制关闭。\";\n        public static LanguageElement LANG_COMMANDS_SOURCES = \"获得当前所有弹幕源列表\";\n        public static LanguageElement LANG_COMMANDS_MSGMGR = \"查看或者设置消息控制器相关内容,添加--help参数获取帮助\";\n        public static LanguageElement LANG_COMMANDS_FILTERS = \"列表所有当前可用消息过滤器\";\n        public static LanguageElement LANG_COMMANDS_DISABLE = \"向插件发送禁用消息 disable (插件名称)\";\n        public static LanguageElement LANG_COMMANDS_SWITCH_CLIENT = \"切换到指定Client实例，不带名称则为获取Client列表\";\n        public static LanguageElement LANG_COMMANDS_SOURCELOGIN = \"登录到弹幕源 sourcelogin [用户名] [密码]\";\n        public static LanguageElement LANG_COMMANDS_RESTART = \"重新启动应用程序\";\n        public static LanguageElement LANG_COMMANDS_LANG = \"lang [cultureName] Get/Set language\";\n        public static LanguageElement LANG_COMMANDS_LISTLANG = \"listlang [--all] List (supported/all) languages\";\n        public static LanguageElement LANG_COMMANDS_FILTERS_ITEM = \"过滤项\";\n        public static LanguageElement LANG_COMMANDS_FILTERS_OBJ = \"过滤器\";\n        public static LanguageElement LANG_COMMANDS_CLIENT_NAME = \"Client\";\n        public static LanguageElement LANG_COMMANDS_CLIENT_AUTHOR = \"作者\";\n        public static LanguageElement LANG_COMMANDS_SOURCES_NAME = \"弹幕源\";\n        public static LanguageElement LANG_COMMANDS_SOURCES_AUTHOR = \"作者\";\n        public static LanguageElement LANG_COMMANDS_CURRENT = \"当前设置为 {0:S}\";\n        public static LanguageElement LANG_COMMANDS_DANMAKU_NOT_SUPPORT = @\"提示：当前弹幕源不支持发送弹幕，请更换弹幕源！\\n\";\n        public static LanguageElement LANG_COMMANDS_CHAT_IRC_NOTCONNECT = \"osu! irc 尚未连接，您还不能发送消息。\";\n        public static LanguageElement LANG_COMMANDS_DANMAKU_REQUIRE_LOGIN = \"你必须登录才能发送弹幕!\";\n        public static LanguageElement LANG_COMMANDS_START_ALREADY_RUN = \"同步实例已经在运行。\";\n        public static LanguageElement LANG_COMMANDS_ARGUMENT_WRONG = \"参数不正确\";\n        public static LanguageElement LANG_COMMANDS_MSGMGR_HELP = @\"\\n--status :查看当前消息管理器的信息\\n--limit <数值> :是设置限制发送信息的等级，越低就越容易触发管控\\n--option <名称> :是设置管控的方式，其中Auto是自动管控，ForceAll强行全都发送,ForceLimit是仅发送使用?send命令的消息,DisableAll是拦截任何管道内的信息\";\n        public static LanguageElement LANG_COMMANDS_MSGMGR_LIMIT = \"限制中...\";\n        public static LanguageElement LANG_COMMANDS_MSGMGR_FREE = \"无限制\";\n        public static LanguageElement LANG_COMMANDS_MSGMGR_STATUS = \"MessageManager mode:{4:S},status:{0:D},queueCount/limitCount/recoverTime:{1}/{2}/{3}\";\n        public static LanguageElement LANG_COMMANDS_MSGMGR_LIMIT_SPEED_SET = \"设置限制发送速度等级为{0}\";\n        public static LanguageElement LANG_COMMANDS_MSGMGR_LIMIT_STYPE_SET = \"设置消息管理器的管制方式为{0}\";\n\n        public static LanguageElement LANG_COMMANDS_START_NO_SOURCE = \"还未钦定任何一个接收源\";\n        public static LanguageElement LANG_COMMANDS_START_NO_CLIENT = \"还未钦定任何一个发送源\";\n        public static LanguageElement LANG_COMMANDS_CURRENT_LANG = \"当前语言: {0:S}\\t{1:S}\";\n        public static LanguageElement LANG_COMMANDS_LANG_SWITCHED = \"成功切换语言至 {1:S}({0:S})\";\n        public static LanguageElement LANG_COMMANDS_LANG_NOT_FOUND = \"切换语言失败,请检查语言代码参数是否正确\";\n\n        public static LanguageElement LANG_UPDATE_DONE = \"更新完成,是否重启软件\";\n        public static LanguageElement LANG_INSTALL_DONE = \"下载完成,是否重启软件\";\n        public static LanguageElement LANG_PLUGIN_NOT_FOUND = \"插件 {0} 不存在\";\n        public static LanguageElement LANG_REMOVE_DONE = \"删除成功,是否重启软件\";\n        public static LanguageElement LANG_VERSION_LATEST_OR_CANEL = \"{0} 已是最新,或者已被用户取消更新操作\";\n        public static LanguageElement LANG_UPDATE_CHECK_ERROR = \"无法根据 [{0}] 检查更新 :  {1} : {2}\";\n        public static LanguageElement LANG_UPDATE_ERROR = \"无法更新 :  {0} : {1}\";\n\n        public static LanguageElement LANG_SOURCE_NOT_SUPPORT_SEND = \"接收源 {0} 并不支持发送功能\";\n        public static LanguageElement LANG_NO_PLUGIN_SELECT = \"还未钦定插件名称\";\n        public static LanguageElement LANG_PLUGIN_DISABLED = \"已禁用 \";\n\n        public static LanguageElement LANG_NO_ANY_SOURCE = \"没有任何弹幕接收源,请检查Plugins目录或使用\\\"plugins install DefaultPlugin\\\"来安装默认插件\";\n        public static LanguageElement LANG_Instance_Exist = \"只能存在一个Sync进程，等待上一个Sync结束\";\n    }\n\n    public interface I18nProvider\n    {\n    }\n\n    public struct LanguageElement\n    {\n        private string value;\n\n        public LanguageElement(string defaultVal)\n        {\n            value = defaultVal;\n        }\n\n        public static implicit operator LanguageElement(string val)\n        {\n            return new LanguageElement(val);\n        }\n\n        public static implicit operator string(LanguageElement element)\n        {\n            return element.value;\n        }\n\n        public override string ToString()\n        {\n            return value;\n        }\n    }\n\n    /// <summary>\n    /// I18n Manager\n    /// </summary>\n    public class I18n\n    {\n        public static string CurrentSystemLang { get => System.Globalization.CultureInfo.CurrentCulture.Name; }\n        private string Base { get => AppDomain.CurrentDomain.BaseDirectory; }\n        public string LangFolder { get => Path.Combine(Base, \"Language\"); }\n        public string SelectLangFolder { get => Path.Combine(LangFolder, CurrentLanguage); }\n        public string CurrentLanguage;\n\n        private static I18n instance;\n\n        private static List<I18nProvider> ApplyedProvider = new List<I18nProvider>();\n\n        public static I18n Instance\n        {\n            get\n            {\n                if (instance == null)\n                {\n                    if (DefaultConfiguration.Instance.Language == DefaultConfiguration.DEFAULT_LANGUAGE || DefaultConfiguration.Instance.Language.ToString().Length == 0)\n                    {\n                        instance = new I18n(CurrentSystemLang);\n                        DefaultConfiguration.Instance.Language = CurrentSystemLang;\n                    }\n                    else\n                    {\n                        instance = new I18n(DefaultConfiguration.Instance.Language);\n                    }\n                }\n                return instance;\n            }\n            private set\n            {\n                instance = value;\n            }\n        }\n\n        public static void SwitchToCulture(string CultureName)\n        {\n            Instance = new I18n(CultureName);\n            foreach (var item in ApplyedProvider)\n            {\n                Instance.ApplyLanguage(item);\n            }\n        }\n\n        private I18n()\n        {\n        }\n\n        /// <summary>\n        ///  Constructor for initial one language\n        /// </summary>\n        /// <param name=\"CultureName\">Cultura name</param>\n        private I18n(string CultureName)\n        {\n            CurrentLanguage = CultureName;\n            if (!Directory.Exists(LangFolder)) Directory.CreateDirectory(LangFolder);\n            if (!Directory.Exists(SelectLangFolder)) Directory.CreateDirectory(SelectLangFolder);\n        }\n\n        public void ApplyLanguage(I18nProvider instance)\n        {\n            if (!ApplyedProvider.Exists(p => p == instance)) ApplyedProvider.Add(instance);\n            string LangFile = Path.Combine(SelectLangFolder, instance.GetType().FullName) + \".lang\";\n            foreach (FieldInfo item in instance.GetType().GetFields())\n            {\n                if (item.FieldType.Equals(typeof(LanguageElement)))\n                {\n                    string value = ConfigurationIO.IniReadValue(LangFile, item.Name, CurrentLanguage);\n                    if (value == \"\")\n                    {\n                        value = (LanguageElement)item.GetValue(instance);\n                        ConfigurationIO.IniWriteValue(LangFile, item.Name, value, CurrentLanguage);\n                    }\n                    item.SetValue(instance, new LanguageElement(value));\n                }\n                else if (item.FieldType.Equals(typeof(GuiLanguageElement)))\n                {\n                    string value = ConfigurationIO.IniReadValue(LangFile, item.Name, CurrentLanguage);\n                    if (value == \"\")\n                    {\n                        value = (GuiLanguageElement)item.GetValue(instance);\n                        ConfigurationIO.IniWriteValue(LangFile, item.Name, value, CurrentLanguage);\n                    }\n                    item.SetValue(instance, new GuiLanguageElement(value));\n                }\n            }\n        }\n\n        public override string ToString() => $\"CurrentLanguage={CurrentLanguage}\";\n    }\n}"
  },
  {
    "path": "Sync/Tools/IConfigurable.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace Sync.Tools\n{\n    public interface IConfigurable\n    {\n        /// <summary>\n        /// Invoke when load configuration\n        /// </summary>\n        void onConfigurationLoad();\n        /// <summary>\n        /// Invoke when save configuration\n        /// </summary>\n        void onConfigurationSave();\n        /// <summary>\n        /// Detected change form files and reload values\n        /// </summary>\n        void onConfigurationReload();\n    }\n}\n"
  },
  {
    "path": "Sync/Tools/PluginConfiuration.cs",
    "content": "﻿using Sync.Plugins;\nusing Sync.Tools;\nusing Sync.Tools.ConfigurationAttribute;\nusing System;\nusing System.Collections.Concurrent;\nusing System.Collections.Generic;\nusing System.Diagnostics;\nusing System.Linq;\nusing System.Reflection;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace Sync.Tools\n{\n    public sealed class ConfigurationElement\n    {\n        private string _cfg = string.Empty;\n\n        public ConfigurationElement()\n        {\n\n        }\n\n        public ConfigurationElement(string def)\n        {\n            _cfg = def;\n        }\n\n        public static implicit operator string(ConfigurationElement e)\n        {\n            return e?._cfg??string.Empty;\n        }\n\n        public static implicit operator ConfigurationElement(string e)\n        {\n            return new ConfigurationElement(e);\n        }\n\n        public override string ToString()\n        {\n            return _cfg;\n        }\n\n        public bool ToBool() => _cfg.ToLower() == \"true\";\n        public int ToInt() => int.Parse(_cfg);\n        public float ToFloat() => float.Parse(_cfg);\n    }\n\n    /// <summary>\n    /// Plugins configuration service, create instance to get configuration service\n    /// </summary>\n    internal sealed class PluginConfiuration\n    {\n        internal string name;\n        internal Plugin instance;\n        internal IConfigurable config;\n\n        public PluginConfiuration(Plugin instance, IConfigurable config):this(instance.Name, config)\n        {\n            this.instance = instance;\n        }\n\n        internal PluginConfiuration(string name, IConfigurable config)\n        {\n            this.name = name;\n            this.config = config;\n            ForceLoad();\n        }\n\n        ~PluginConfiuration()\n        {\n            ForceSave();\n        }\n        \n        internal void Load()\n        {\n            var configType = config.GetType();\n            var holderAttribute = configType.GetCustomAttribute<ConfigurationHolderAttribute>();\n\n            foreach (PropertyInfo item in configType.GetProperties())\n            {\n                if (item.PropertyType == typeof(ConfigurationElement))\n                {\n                    ConfigurationElement element = ConfigurationIO.Read(item.Name, name + \".\" + config.GetType().Name/*,item.GetValue(config).ToString()*/);\n\n                    if (!string.IsNullOrWhiteSpace(element))\n                    {\n                        if (CheckValueVaild(item, element, holderAttribute))\n                        {\n                            item.SetValue(config, element);\n                        }\n                    }\n                    else\n                    {\n                        //if not exist,write to config.ini immediately\n                        ConfigurationIO.Write(item.Name, (ConfigurationElement)item.GetValue(config), name + \".\" + config.GetType().Name);\n                    }\n                }\n            }\n        }\n\n        internal void ForceLoad()\n        {\n            Load();\n            config.onConfigurationLoad();\n        }\n        \n        private bool CheckValueVaild(PropertyInfo info, ConfigurationElement element, ConfigurationHolderAttribute classHolder)\n        {\n            var configAttribute = info.GetCustomAttribute<BaseConfigurationAttribute>();\n            bool noCheck = configAttribute?.NoCheck ?? classHolder?.NoCheck ?? true;\n\n            if (configAttribute == null)\n                return true;\n\n            if (!noCheck)\n                if (!configAttribute.Check(element))\n                {\n                    configAttribute.CheckFailedNotify(element);\n                    return false;\n                }\n\n            return true;\n        }\n\n        internal void ForceReload()\n        {\n            Load();\n            config.onConfigurationReload();\n        }\n\n\n        internal void ForceSave()\n        {\n            config.onConfigurationSave();\n            foreach (PropertyInfo item in config.GetType().GetProperties())\n            {\n                if (item.PropertyType == typeof(ConfigurationElement))\n                {\n                    ConfigurationIO.Write(item.Name, (ConfigurationElement)item.GetValue(config), name + \".\" + config.GetType().Name);\n                }\n            }\n        }\n    }\n\n    /// <summary>\n    /// Configuration Manager\n    /// </summary>\n    public sealed class PluginConfigurationManager\n    {\n        internal static ConcurrentBag<PluginConfigurationManager> ConfigurationSet = new ConcurrentBag<PluginConfigurationManager>();\n        internal static bool InSaving = false;\n        internal List<PluginConfiuration> items;\n        internal Plugin instance;\n        internal string name;\n\n        public PluginConfigurationManager(Plugin plugin):this(plugin.Name)\n        {\n            instance = plugin;\n        }\n\n        internal PluginConfigurationManager(string name)\n        {\n            items = new List<PluginConfiuration>();\n            this.name = name;\n            ConfigurationSet.Add(this);\n        }\n\n        public void AddItem(IConfigurable Config)\n        {\n            items.Add(new PluginConfiuration(name, Config));\n        }\n\n        internal void ReloadAll()\n        {\n            foreach (var item in items)\n            {\n                item.ForceReload();\n            }\n        }\n\n        public void SaveAll()\n        {\n            InSaving = true;\n            foreach (var item in items)\n            {\n                item.ForceSave();\n            }\n            InSaving = false;\n        }\n\n        //public PluginConfiuration GetInstance(IConfigurable obj)\n        //{\n        //    foreach (var item in items)\n        //    {\n        //        if (item.GetType() == obj.GetType()) return item;\n        //    }\n        //    return null;\n        //}\n\n        ~PluginConfigurationManager () => SaveAll();\n    }\n}\n"
  },
  {
    "path": "Sync/Tools/SentryHelper.cs",
    "content": "﻿using SharpRaven;\r\nusing SharpRaven.Data;\r\nusing Sync.Plugins;\r\nusing System;\r\nusing System.Collections.Generic;\r\nusing System.Diagnostics;\r\nusing System.Linq;\r\nusing System.Reflection;\r\nusing System.Text;\r\nusing System.Threading.Tasks;\r\n\r\nnamespace Sync.Tools\r\n{\r\n\r\n    /// <summary>\r\n    /// A standard error reporter for plugins\r\n    /// </summary>\r\n    /// <typeparam name=\"T\">Plugin Error impl</typeparam>\r\n    public abstract class ErrorRepoter<T> where T : Plugin\r\n    {\r\n        private Action<Exception> errorHandler;\r\n        public ErrorRepoter()\r\n        {\r\n            errorHandler = SentryHelper.Instance.RegisterErrorReporter(typeof(T));\r\n        }\r\n\r\n        public void ReportException(Exception e) => errorHandler(e);\r\n    }\r\n\r\n    /// <summary>\r\n    /// Sentry helper for error reporter\r\n    /// </summary>\r\n    internal class SentryHelper\r\n    {\r\n        private RavenClient ravenClient = new RavenClient(\"https://955afb316e2c4a2eaa4070c18071b0c0@sentry.io/1276032\");\r\n        private Dictionary<Plugins.Plugin, object> registedErrorReporter = new Dictionary<Plugins.Plugin, object>();\r\n#if SyncRelease\r\n        private const bool notInDebugger = true;\r\n#else\r\n        private const bool notInDebugger = false;\r\n#endif\r\n        private SentryHelper()\r\n        {\r\n        }\r\n\r\n        internal Action<Exception> RegisterErrorReporter(Type type)\r\n        {\r\n            if (type == null ||\r\n                !type.IsClass || !type.IsPublic ||\r\n                !typeof(Plugin).IsAssignableFrom(type) ||\r\n                typeof(Plugin) == type)\r\n            {\r\n                return (x) => Console.WriteLine(\"Type must inherit Plugin class\");\r\n            }\r\n            else\r\n            {\r\n                var logger = type.FullName;\r\n                var version = type.Assembly.GetName().Version.ToString();\r\n                return (x) => this.Error(logger, version, x);\r\n            }\r\n        }\r\n\r\n        internal void Error(string logger, string version, Exception e, bool silent = false)\r\n        {\r\n            ravenClient.Logger = logger;\r\n            ravenClient.Release = version;\r\n            var error = new SentryEvent(e);\r\n            if (notInDebugger)\r\n            {\r\n                if (!silent)\r\n                    Console.WriteLine(\"Opps! You seem occur a error! We was captured this error and repoting to developers\");\r\n                ravenClient.Capture(error);\r\n            }\r\n            else\r\n            {\r\n                Console.WriteLine(\"A error was raised but not repoted to Sync developer\");\r\n                Console.WriteLine(\"Now a error report will print, you can report in github issue\");\r\n                Console.WriteLine(error.Message.ToString());\r\n                Console.WriteLine($\"Scope: {logger}, Version: {version}\");\r\n                Console.WriteLine(\"- TRACE -\");\r\n                Console.WriteLine(e.StackTrace);\r\n            }\r\n        }\r\n\r\n        internal void RepoterError(Exception e, bool silent = false) => Error(\"Sync\", Assembly.GetEntryAssembly().GetName().Version.ToString(), e, silent);\r\n\r\n        internal static readonly SentryHelper Instance = new SentryHelper();\r\n    }\r\n}\r\n"
  },
  {
    "path": "Sync/Tools/StartupArgument.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace Sync.Tools\n{\n    internal static class StartupArgument\n    {\n        private static Action<StartupHelper> ForceStartArg(string arg) => (_) => StartupHelper.ForceStart = true;\n        private static Action<StartupHelper> NeedUpdateSyncArg(string arg) => (_) => StartupHelper.NeedUpdateSync = true;\n\n        internal static Dictionary<string, Func<string, Action<StartupHelper>>> Arguments = new Dictionary<string, Func<string, Action<StartupHelper>>>()\n        {\n            { \"f\", ForceStartArg },\n            { \"--force-start\", ForceStartArg },\n            { \"u\", NeedUpdateSyncArg },\n            { \"--update\", NeedUpdateSyncArg }\n        };\n\n        internal static Dictionary<string, Func<string, Action<StartupHelper>>> Actions = new Dictionary<string, Func<string, Action<StartupHelper>>>();\n    }\n}\n"
  },
  {
    "path": "Sync/Tools/StartupHelper.cs",
    "content": "﻿using Sync.Tools;\nusing System;\nusing System.Linq;\nusing System.Collections.Generic;\nusing System.Diagnostics;\nusing System.IO;\nusing System.IO.MemoryMappedFiles;\nusing System.Runtime.InteropServices;\nusing System.Threading;\nusing static Sync.Tools.IO;\n\nnamespace Sync.Tools\n{\n    public class StartupHelper\n    {\n        public const string SYNC_GUID = \"{781d2da2-1b44-46d9-8b01-e1d59adc018b}\";\n\n        internal delegate bool ControlCtrlDelegate(int CtrlType);\n        [DllImport(\"kernel32.dll\")]\n        internal static extern bool SetConsoleCtrlHandler(ControlCtrlDelegate HandlerRoutine, bool Add);\n        internal static ControlCtrlDelegate cancelHandler = new ControlCtrlDelegate(HandlerRoutine);\n\n        private static bool HandlerRoutine(int CtrlType)\n        {\n            CurrentIO.Write(\"Exiting...\");\n            SyncHost.Instance?.SaveSync();\n            CurrentIO.Write(\"Saved.\");\n            SetConsoleCtrlHandler(cancelHandler, false);\n            return true;\n        }\n \n        private void SyncInstanceLocker(MemoryMappedFile syncMappedFile, bool forceExit)\n        {\n            using (var syncViewStream = syncMappedFile.CreateViewStream())\n            {\n                int pid = 0;\n                using (var reader = new BinaryReader(syncViewStream))\n                {\n                    pid = reader.ReadInt32();\n\n                    if (pid != 0)\n                    {\n                        var oldSync = Process.GetProcesses().FirstOrDefault((proc) => proc.Id == pid);\n                        if (oldSync != null)\n                        {\n                            if (forceExit)\n                            {\n                                oldSync.Kill();\n                            }\n                            else\n                            {\n                                CurrentIO.WriteColor(DefaultI18n.LANG_Instance_Exist, ConsoleColor.Red);\n                                int limit = 50, current = 0;\n                                while (!oldSync.WaitForExit(100))\n                                {\n                                    current++;\n                                    if (current > limit) break;\n                                }\n\n                                if (!oldSync.WaitForExit(100))\n                                {\n                                    oldSync.Kill();\n                                }\n                            }\n                        }\n                    }\n\n                    using (var writer = new BinaryWriter(syncViewStream))\n                    {\n                        writer.Seek(0, SeekOrigin.Begin);\n                        writer.Write(Process.GetCurrentProcess().Id);\n                    }\n                }\n            }\n        }\n\n        static void PreInitSync()\n        {\n            //Initialize I18n\n            I18n.Instance.ApplyLanguage(new DefaultI18n());\n        }\n\n        static void InitSync()\n        {\n            //Apply update\n            if (Updater.ApplyUpdate(NeedUpdateSync))\n                Environment.Exit(0);\n\n            //Add Console close event handler\n            SetConsoleCtrlHandler(cancelHandler, true);\n\n            //Initialize Sync core\n            SyncHost.Instance = new SyncHost();\n            SyncHost.Instance.Load();\n\n            //Sync ready message to all plugins\n            SyncHost.Instance.Plugins.ReadySync();\n\n            //Check update\n            if (DefaultConfiguration.Instance.CheckUpdateOnStartup.ToBool())\n                Updater.update.SyncUpdateCheck();\n\n            //Sync program update check\n            if (Updater.IsUpdated)\n                CurrentIO.WriteColor(\"Sync is already up to date!\", ConsoleColor.Green);\n\n        }\n\n        public static bool ForceStart { get; internal set; }\n        public static bool NeedUpdateSync { get; internal set; }\n\n        internal StartupHelper(string[] args)\n        {\n            (new CommandParser<StartupHelper>(args, StartupArgument.Arguments, StartupArgument.Actions)).ExecuteActionOn(this);\n        }\n\n        internal void Start()\n        {\n            //Check sync.exe is run\n            using (var syncMappedFile = MemoryMappedFile.CreateOrOpen(SYNC_GUID, 4))\n            {\n                PreInitSync();\n                SyncInstanceLocker(syncMappedFile, ForceStart);\n\n                InitSync();\n                CurrentIO.WriteWelcome();\n\n                while (true)\n                {\n                    var cmd = CurrentIO.ReadCommand();\n                    SyncHost.Instance.Commands.invokeCmdString(cmd);\n                }\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "Sync/Tools/StringElement.cs",
    "content": "﻿using System;\n\nnamespace Sync.Tools\n{\n    /// <summary>\n    /// A helper for a string with a perfix and a suffix\n    /// </summary>\n    public struct StringElement\n    {\n        public string perfix;\n        public string str;\n        public string suffix;\n\n        public StringElement(string perfix, string text, string suffix)\n        {\n            this.perfix = perfix;\n            str = text;\n            if (str == null) str = string.Empty;\n            this.suffix = suffix;\n        }\n\n        public StringElement(string perfix, string text)\n        {\n            this.perfix = perfix;\n            str = text;\n            if (str == null) str = string.Empty;\n            suffix = string.Empty;\n        }\n\n        public StringElement(string source)\n        {\n            str = source;\n            if (str == null) str = string.Empty;\n            perfix = string.Empty;\n            suffix = string.Empty;\n        }\n\n        public string Result\n        {\n            get\n            {\n                return str.Length == 0 ? String.Empty : perfix + str + suffix;\n            }\n        }\n\n        public string RawText\n        {\n            get { return str; }\n        }\n\n        public string Perfix\n        {\n            get { return perfix; }\n        }\n\n        public string Suffix\n        {\n            get { return suffix; }\n        }\n\n        public static StringElement operator +(StringElement op1, StringElement op2)\n        {\n            if (op1 == null) return op2;\n            if (op2 == null) return op1;\n            return new StringElement(op1.perfix + op2.perfix, op1.str + op2.str, op1.suffix + op2.suffix);\n        }\n\n        public static string operator +(StringElement op1, string op2)\n        {\n\n            return op1.Result + op2;\n        }\n\n        public static implicit operator StringElement(string e)\n        {\n            return new StringElement(e);\n        }\n\n        public static implicit operator string(StringElement e)\n        {\n            return e.Result;\n        }\n\n        public override string ToString()\n        {\n            return Result;\n        }\n    }\n}\n"
  },
  {
    "path": "Sync/Tools/SyncIO/ConsoleWriter.cs",
    "content": "﻿using System;\n\nnamespace Sync.Tools\n{\n    [Obsolete]\n    public interface ISyncIO : ISyncOutput, ISyncInput\n    {\n    }\n\n    public interface ISyncConsoleWriter : ISyncOutput, ISyncInput\n    {\n    }\n\n    public interface ISyncOutput\n    {\n        void Write(string msg, bool newline = true, bool time = true);\n\n        void WriteColor(string text, ConsoleColor color, bool newline = true, bool time = true);\n\n        void WriteHelp(string cmd, string desc);\n\n        void WriteHelp();\n\n        void WriteStatus();\n\n        void WriteWelcome();\n\n        void Clear();\n    }\n\n    public interface ISyncInput\n    {\n        string ReadCommand();\n    }\n\n    public static class IO\n    {\n        public static readonly NConsoleWriter DefaultIO = new NConsoleWriter();\n        public static readonly FileLoggerWriter FileLogger;\n        public static readonly IOWrapper CurrentIO = new IOWrapper();\n\n        [Obsolete(\"Obsoleted, instead with AddOutput and SetInput\", true)]\n        public static void SetIO(ISyncIO specIO)\n        {\n            CurrentIO.SetInput(specIO);\n            CurrentIO.AddOutput(specIO);\n        }\n\n        public static void SetIO(ISyncConsoleWriter specIO)\n        {\n            CurrentIO.SetInput(specIO);\n            CurrentIO.AddOutput(specIO);\n        }\n\n        static IO()\n        {\n            SetIO(DefaultIO);\n            try\n            {\n                FileLogger = new FileLoggerWriter();\n                AddOutput(FileLogger);\n            }\n            catch\n            {\n                DefaultIO.Write(\"Initial File Logger failed!!\");\n            }\n        }\n\n        public static void AddOutput(ISyncOutput output) => CurrentIO.AddOutput(output);\n\n        public static void SetInput(ISyncInput input) => CurrentIO.SetInput(input);\n    }\n}"
  },
  {
    "path": "Sync/Tools/SyncIO/FileLoggerWriter.cs",
    "content": "﻿using System;\r\nusing System.IO;\r\nusing static Sync.Tools.DefaultI18n;\r\n\r\nnamespace Sync.Tools\r\n{\r\n    public class FileLoggerWriter : ISyncOutput\r\n    {\r\n        private StreamWriter logger;\r\n\r\n        internal FileLoggerWriter()\r\n        {\r\n            if (DefaultConfiguration.Instance.LogDirectory == \"\")\r\n            {\r\n                DefaultConfiguration.Instance.LogDirectory = @\"Logs\\\";\r\n            }\r\n\r\n            if(DefaultConfiguration.Instance.LogFilename == \"\")\r\n            {\r\n                DefaultConfiguration.Instance.LogFilename = \"Log-{0}.txt\";\r\n            }\r\n\r\n            string date = DateTime.Now.ToString(\"yyyy-MM-dd@hh.mm.ss\");\r\n            string log = string.Format(Path.Combine(DefaultConfiguration.Instance.LogDirectory, DefaultConfiguration.Instance.LogFilename), date);\r\n            if (!Path.IsPathRooted(log))\r\n                log = Path.Combine(AppDomain.CurrentDomain.BaseDirectory,log);\r\n            Directory.CreateDirectory(Path.GetDirectoryName(log));\r\n\r\n            logger = new StreamWriter(File.Open(log, FileMode.OpenOrCreate, FileAccess.Write))\r\n            { AutoFlush = true };\r\n        }\r\n\r\n        public void Clear()\r\n        {\r\n        }\r\n\r\n        public void Write(string msg, bool newline = true, bool time = true)\r\n        {\r\n            string ms = System.Text.RegularExpressions.Regex.Replace(msg, @\"\\\\t|\\\\n\", m =>\r\n            {\r\n                switch (m.ToString())\r\n                {\r\n                    case @\"\\t\": return \"\\t\";\r\n                    case @\"\\n\": return \"\\n\";\r\n                }\r\n                return m.ToString();\r\n            });\r\n            logger.Write((time ? \"[\" + DateTime.Now.ToLongTimeString() + \"] \" : \"\")\r\n               + ms\r\n               + (newline ? \"\\n\" : \"\"));\r\n        }\r\n\r\n        public void WriteColor(string text, ConsoleColor color, bool newline = true, bool time = true)\r\n        {\r\n            Write(text, newline, time);\r\n        }\r\n\r\n        public void WriteHelp(string cmd, string desc)\r\n        {\r\n        }\r\n\r\n        public void WriteHelp()\r\n        {\r\n        }\r\n\r\n        public void WriteStatus()\r\n        {\r\n        }\r\n\r\n        public void WriteWelcome()\r\n        {\r\n            Write(string.Format(LANG_Welcome,\r\n                   System.Reflection.Assembly.GetExecutingAssembly().GetName().Version.ToString()));\r\n\r\n            Write(LANG_Help);\r\n        }\r\n    }\r\n}"
  },
  {
    "path": "Sync/Tools/SyncIO/IOWrapper.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\n\nnamespace Sync.Tools\n{\n    public sealed class IOWrapper : ISyncConsoleWriter\n    {\n        private ISyncInput currI;\n        private List<ISyncOutput> currOs = new List<ISyncOutput>();\n\n        public void Clear() => currOs.ForEach(p => p.Clear());\n\n        public string ReadCommand() => currI.ReadCommand();\n\n        public void Write(string msg, bool newline = true, bool time = true) => currOs.ForEach(p => p.Write(msg, newline, time));\n\n        public void WriteColor(string text, ConsoleColor color, bool newline = true, bool time = true) => currOs.ForEach(p => p.WriteColor(text, color, newline, time));\n\n        public void WriteHelp(string cmd, string desc) => currOs.ForEach(p => p.WriteHelp(cmd, desc));\n\n        public void WriteHelp() => currOs.ForEach(p => p.WriteHelp());\n\n        public void WriteStatus() => currOs.ForEach(p => p.WriteStatus());\n\n        public void WriteWelcome() => currOs.ForEach(p => p.WriteWelcome());\n\n        internal void SetInput(ISyncInput input)\n        {\n            this.currI = input;\n        }\n\n        internal void AddOutput(ISyncOutput output)\n        {\n            if (currOs.Contains(output)) return;\n            currOs.Add(output);\n        }\n    }\n}"
  },
  {
    "path": "Sync/Tools/SyncIO/Logger.cs",
    "content": "﻿using System;\n\nnamespace Sync.Tools\n{\n    public enum LogType\n    {\n        Infomation,\n        Warning,\n        Error,\n    }\n\n    public class Logger\n    {\n        private static ConsoleColor[] LOGTYPE_COLORS = new[]\n        {\n            ConsoleColor.Green,\n            ConsoleColor.Yellow,\n            ConsoleColor.Red\n        };\n\n        private readonly string prefix;\n        private readonly ISyncOutput output;\n\n        public Logger(string prefix, ISyncOutput output = null)\n        {\n            this.prefix = prefix;\n            this.output = output ?? IO.CurrentIO;\n        }\n\n        public void Log(string message, LogType type) => output?.WriteColor($\"[{prefix}]{message}\", LOGTYPE_COLORS[(int)type]);\n\n        public void LogInfomation(string message) => Log(message, LogType.Infomation);\n\n        public void LogWarning(string message) => Log(message, LogType.Warning);\n\n        public void LogError(string message) => Log(message, LogType.Error);\n    }\n\n    public class Logger<T> : Logger\n    {\n        public Logger(ISyncOutput output = null) : base(typeof(T).Name, output)\n        {\n        }\n    }\n}"
  },
  {
    "path": "Sync/Tools/SyncIO/NConsoleWriter.cs",
    "content": "﻿using System;\r\nusing static Sync.Tools.DefaultI18n;\r\n\r\nnamespace Sync.Tools\r\n{\r\n    public class NConsoleWriter : ISyncConsoleWriter, ISyncOutput, ISyncInput\r\n    {\r\n        private bool wait = false;\r\n\r\n        /// <summary>\r\n        /// Read Line\r\n        /// </summary>\r\n        /// <returns>Input chars</returns>\r\n        public string ReadCommand()\r\n        {\r\n            WriteColor(\">\", ConsoleColor.Green, false, false);\r\n            wait = true;\r\n            return Console.ReadLine();\r\n        }\r\n\r\n        /// <summary>\r\n        /// Write a message to console\r\n        /// </summary>\r\n        /// <param name=\"msg\">Message</param>\r\n        /// <param name=\"newline\">Display in new line</param>\r\n        public void Write(string msg, bool newline = true, bool time = true)\r\n        {\r\n            if (wait)\r\n            {\r\n                wait = false;\r\n                Console.SetCursorPosition(0, Console.CursorTop);\r\n            }\r\n\r\n            string ms = System.Text.RegularExpressions.Regex.Replace(msg, @\"\\\\t|\\\\n\", m =>\r\n            {\r\n                switch (m.ToString())\r\n                {\r\n                    case @\"\\t\": return \"\\t\";\r\n                    case @\"\\n\": return \"\\n\";\r\n                }\r\n                return m.ToString();\r\n            });\r\n\r\n            Console.Write((time ? \"[\" + DateTime.Now.ToLongTimeString() + \"] \" : \"\")\r\n               + ms\r\n               + (newline ? \"\\n\" : \"\"));\r\n        }\r\n\r\n        /// <summary>\r\n        /// Write a message with color\r\n        /// </summary>\r\n        /// <param name=\"text\">Message</param>\r\n        /// <param name=\"color\">Color</param>\r\n        /// <param name=\"newline\">Display in new line</param>\r\n        public void WriteColor(string text, ConsoleColor color, bool newline = true, bool time = true)\r\n        {\r\n            Console.ForegroundColor = color;\r\n            Write(text, newline, time);\r\n            Console.ResetColor();\r\n        }\r\n\r\n        /// <summary>\r\n        /// Write a formated help message\r\n        /// </summary>\r\n        /// <param name=\"cmd\">命令</param>\r\n        /// <param name=\"desc\">命令描述</param>\r\n        public void WriteHelp(string cmd, string desc)\r\n        {\r\n            WriteColor(cmd.PadRight(10), ConsoleColor.Cyan, false, false);\r\n            WriteColor(desc, ConsoleColor.White, true, false);\r\n        }\r\n\r\n        /// <summary>\r\n        /// Write current work status\r\n        /// </summary>\r\n        public void WriteStatus()\r\n        {\r\n            WriteColor(\"Source:\" + SyncHost.Instance.SourceWrapper.Source?.Status.ToString(), ConsoleColor.Magenta);\r\n            WriteColor(\"Client:\" + SyncHost.Instance.ClientWrapper.Client?.CurrentStatus.ToString(), ConsoleColor.Magenta);\r\n        }\r\n\r\n        /// <summary>\r\n        /// Write welcolme message\r\n        /// </summary>\r\n        public void WriteWelcome()\r\n        {\r\n            Write(string.Format(LANG_Welcome,\r\n                   System.Reflection.Assembly.GetExecutingAssembly().GetName().Version.ToString()));\r\n\r\n            Write(LANG_Help);\r\n        }\r\n\r\n        /// <summary>\r\n        /// Write all commands\r\n        /// </summary>\r\n        public void WriteHelp()\r\n        {\r\n            WriteHelp(LANG_Command, LANG_Command_Description);\r\n            WriteHelp(\"======\", \"======\");\r\n            foreach (var item in SyncHost.Instance.Commands.Dispatch.getCommandsHelp())\r\n            {\r\n                WriteHelp(item.Key, item.Value);\r\n            }\r\n            WriteHelp(\"======\", \"======\");\r\n            Write(\"\", true, false);\r\n        }\r\n\r\n        /// <summary>\r\n        /// Clear screen\r\n        /// </summary>\r\n        public void Clear()\r\n        {\r\n            Console.Clear();\r\n        }\r\n    }\r\n}"
  },
  {
    "path": "Sync/Tools/Updater.cs",
    "content": "﻿using Sync.Tools.Builtin;\nusing Sync.Tools;\nusing System;\nusing System.Collections.Generic;\nusing System.Diagnostics;\nusing System.IO;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace Sync.Tools\n{\n    static class Updater\n    {\n        public const string SourceEXEName = \"Sync.exe\";\n        public const string UpdateEXEName = \"Sync_update.exe\";\n        public const string UpdateArg = \"--update\";\n        public static readonly string CurrentEXEName = Path.GetFileName(Process.GetCurrentProcess().Modules[0].FileName);\n        public static readonly string CurrentPath = AppDomain.CurrentDomain.BaseDirectory;\n        public static readonly string CurrentFullEXEPath = Path.Combine(CurrentPath, CurrentEXEName);\n        public static readonly string CurrentFullSourceEXEPath = Path.Combine(CurrentPath, SourceEXEName);\n        public static readonly string CurrentFullUpdateEXEPath = Path.Combine(CurrentPath, UpdateEXEName);\n        public static bool IsUpdated = false;\n        internal static PluginCommand update => PluginCommand.Instance;\n\n        public static bool ApplyUpdate(bool needUpdate)\n        {\n            if (CurrentEXEName == SourceEXEName)\n            {\n                if (needUpdate)\n                {\n                    Process.GetProcessesByName(UpdateEXEName).FirstOrDefault(p => p.MainModule.FileName.Contains(CurrentFullUpdateEXEPath))?.Kill();\n                    File.Delete(CurrentFullUpdateEXEPath);\n                    IsUpdated = true;\n                }\n                else if (File.Exists(CurrentFullUpdateEXEPath))\n                {\n                    try\n                    {\n                        Process.Start(CurrentFullUpdateEXEPath);\n                        return true;\n                    }\n                    catch (Exception e)\n                    {\n                        File.Delete(Path.Combine(CurrentPath, UpdateEXEName));\n                        IO.CurrentIO.WriteColor($\"Apply update fail! Can't launch ({e.Message})\", ConsoleColor.Red);\n                        return false;\n                    }\n                }\n            }\n            else if (CurrentEXEName == UpdateEXEName)\n            {\n                Process.GetProcessesByName(SourceEXEName).FirstOrDefault(p => p.MainModule.FileName.Contains(CurrentFullSourceEXEPath))?.Kill();\n                File.Delete(CurrentFullSourceEXEPath);\n                File.Copy(CurrentFullUpdateEXEPath, CurrentFullSourceEXEPath);\n                Process.Start(CurrentFullSourceEXEPath, UpdateArg);\n                return true;\n            }\n\n            return false;\n        }\n    }\n}\n"
  },
  {
    "path": "Sync/Tools/Utils/ConfigurationHelper.cs",
    "content": "﻿using Sync.Plugins;\nusing System;\nusing System.Collections;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Reflection;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace Sync.Tools.Utils\n{\n    public static class ConfigurationHelper\n    {\n        /// <summary>\n        /// dymatic to get config value from other plugin by reflection.\n        /// </summary>\n        /// <param name=\"main_section_name\">default as plugin name</param>\n        /// <param name=\"sub_section_name\">default as IConfigurable name</param>\n        /// <param name=\"config_name\"></param>\n        /// <returns></returns>\n        public static bool TryGetConfigurationElement(string main_section_name,string sub_section_name, string config_name,out ConfigurationElement result)\n        {\n            foreach(var manager in PluginConfigurationManager.ConfigurationSet)\n            {\n                if (main_section_name == manager.name)\n                {\n                    foreach (var plugin_config in manager.items)\n                    {\n                        var config_instance = plugin_config.config;\n                        //sub section name\n                        var name = config_instance.GetType().Name;\n\n                        if (name==sub_section_name)\n                        {\n                            var config_type = config_instance.GetType();\n\n                            foreach (var prop in config_type.GetProperties())\n                            {\n                                if (prop.Name == config_name)\n                                {\n                                    //copy\n                                    result = prop.GetValue(config_instance).ToString();\n                                    return true;\n                                }\n                            }\n                        }\n                    }\n                }\n            }\n\n            result = null;\n            return false;\n        }\n    }\n}\n"
  },
  {
    "path": "Sync/Tools/Utils/PluginsHelper.cs",
    "content": "﻿using Sync.Plugins;\nusing System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace Sync.Tools.Utils\n{\n    public static class PluginsHelper\n    {\n        public static bool TryGetPlugin<T>(out T plugin) where T : Plugin\n        {\n            plugin = SyncHost.Instance.Plugins.GetPlugins().FirstOrDefault(p => p is T) as T;\n            return plugin != null;\n        }\n    }\n}\n"
  },
  {
    "path": "Sync/app.config",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<configuration>\n<startup><supportedRuntime version=\"v4.0\" sku=\".NETFramework,Version=v4.5\"/></startup></configuration>\n"
  },
  {
    "path": "Sync/config.ini",
    "content": "[config]\nLiveRoomID=\nTargetIRC=\nBotIRC=\nBotIRCPassword=\nProvider=Bilibili"
  },
  {
    "path": "Sync/packages.config",
    "content": "﻿<?xml version=\"1.0\" encoding=\"utf-8\"?>\r\n<packages>\r\n  <package id=\"Newtonsoft.Json\" version=\"6.0.8\" targetFramework=\"net45\" />\r\n  <package id=\"SharpRaven\" version=\"2.4.0\" targetFramework=\"net45\" />\r\n  <package id=\"System.Runtime.InteropServices.RuntimeInformation\" version=\"4.3.0\" targetFramework=\"net45\" />\r\n</packages>"
  },
  {
    "path": "Sync.sln",
    "content": "﻿\nMicrosoft Visual Studio Solution File, Format Version 12.00\n# Visual Studio 15\nVisualStudioVersion = 15.0.26403.3\nMinimumVisualStudioVersion = 10.0.40219.1\nProject(\"{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}\") = \"Sync\", \"Sync\\Sync.csproj\", \"{FBD514C2-2830-479E-B050-D1C383028456}\"\nEndProject\n\nGlobal\n\tGlobalSection(SolutionConfigurationPlatforms) = preSolution\n\t\tDebug|Any CPU = Debug|Any CPU\n\t\tDebug|x86 = Debug|x86\n\t\tRelease|Any CPU = Release|Any CPU\n\t\tRelease|x86 = Release|x86\n\tEndGlobalSection\n\tGlobalSection(ProjectConfigurationPlatforms) = postSolution\n\t\t{FBD514C2-2830-479E-B050-D1C383028456}.Debug|Any CPU.ActiveCfg = Debug|Any CPU\n\t\t{FBD514C2-2830-479E-B050-D1C383028456}.Debug|Any CPU.Build.0 = Debug|Any CPU\n\t\t{FBD514C2-2830-479E-B050-D1C383028456}.Debug|x86.ActiveCfg = Debug|Any CPU\n\t\t{FBD514C2-2830-479E-B050-D1C383028456}.Debug|x86.Build.0 = Debug|Any CPU\n\t\t{FBD514C2-2830-479E-B050-D1C383028456}.Release|Any CPU.ActiveCfg = Release|Any CPU\n\t\t{FBD514C2-2830-479E-B050-D1C383028456}.Release|Any CPU.Build.0 = Release|Any CPU\n\t\t{FBD514C2-2830-479E-B050-D1C383028456}.Release|x86.ActiveCfg = Release|Any CPU\n\t\t{FBD514C2-2830-479E-B050-D1C383028456}.Release|x86.Build.0 = Release|Any CPU\n\tEndGlobalSection\n\tGlobalSection(SolutionProperties) = preSolution\n\t\tHideSolutionNode = FALSE\n\tEndGlobalSection\nEndGlobal\n"
  }
]