diff --git a/CLI/App.config b/CLI/App.config
new file mode 100644
index 000000000..0ebea3d13
--- /dev/null
+++ b/CLI/App.config
@@ -0,0 +1,27 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/CLI/BaseCommand.cs b/CLI/BaseCommand.cs
new file mode 100644
index 000000000..ba5cf4816
--- /dev/null
+++ b/CLI/BaseCommand.cs
@@ -0,0 +1,63 @@
+using System;
+using System.IO;
+using System.Threading.Tasks;
+
+namespace CLI
+{
+ internal abstract class BaseCommand
+ {
+ private readonly string _name;
+ private readonly string _description;
+ public abstract Task Run(string[] args);
+ public abstract void PrintHelp();
+
+ protected BaseCommand(string name, string description = "")
+ {
+ _name = name;
+ _description = description;
+ }
+
+ public string GetName()
+ {
+ return _name;
+ }
+
+ public string GetDescription()
+ {
+ return _description;
+ }
+
+ public void ParseArgs(string[] args)
+ {
+ foreach (string arg in args)
+ {
+ if (arg == "--help")
+ {
+ PrintHelp();
+ Environment.Exit(0);
+ }
+ }
+ }
+
+ protected bool IsCurrentDirectoryWritable()
+ {
+ return IsDirectoryWritable(Directory.GetCurrentDirectory());
+ }
+
+ protected bool IsDirectoryWritable(string directoryPath)
+ {
+ try
+ {
+ using (FileStream fs = File.Create(Path.Combine(directoryPath, Path.GetRandomFileName()), 1, FileOptions.DeleteOnClose))
+ {
+ }
+
+ return true;
+ }
+ catch
+ {
+ return false;
+ }
+ }
+ }
+}
diff --git a/CLI/CLI.csproj b/CLI/CLI.csproj
new file mode 100644
index 000000000..78c60da02
--- /dev/null
+++ b/CLI/CLI.csproj
@@ -0,0 +1,91 @@
+
+
+
+
+ Debug
+ AnyCPU
+ {86541EF7-D99F-46CC-8836-020D0AE1BCE9}
+ Exe
+ CLI
+ csgodm
+ v4.6.2
+ 512
+ true
+ true
+
+
+ AnyCPU
+ true
+ full
+ false
+ bin\Debug\
+ DEBUG;TRACE
+ prompt
+ 4
+
+
+ AnyCPU
+ pdbonly
+ true
+ bin\Release\
+ TRACE
+ prompt
+ 4
+
+
+ app.ico
+
+
+
+ ..\packages\CommonServiceLocator.2.0.6\lib\net46\CommonServiceLocator.dll
+
+
+ ..\packages\MvvmLightLibs.5.4.1.1\lib\net45\GalaSoft.MvvmLight.dll
+
+
+ ..\packages\MvvmLightLibs.5.4.1.1\lib\net45\GalaSoft.MvvmLight.Extras.dll
+
+
+ ..\packages\MvvmLightLibs.5.4.1.1\lib\net45\GalaSoft.MvvmLight.Platform.dll
+
+
+
+
+ ..\packages\MvvmLightLibs.5.4.1.1\lib\net45\System.Windows.Interactivity.dll
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {4180E444-4AA1-4564-9296-50323FE504FE}
+ Core
+
+
+ {B0B2F0B5-4E7F-41AE-817F-9C0F30DA5367}
+ Services
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/CLI/DownloadCommand.cs b/CLI/DownloadCommand.cs
new file mode 100644
index 000000000..70d8b00ac
--- /dev/null
+++ b/CLI/DownloadCommand.cs
@@ -0,0 +1,265 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Threading;
+using System.Threading.Tasks;
+using Core;
+using Services.Concrete;
+using Services.Exceptions;
+using Services.Interfaces;
+
+namespace CLI
+{
+ internal class DownloadCommand : BaseCommand
+ {
+ public const string COMMAND_NAME = "download";
+ private string _outputFolderPath;
+ private readonly ISteamService _steamService;
+ private readonly IDemosService _demoService;
+ private readonly CancellationTokenSource _cts;
+ private readonly List _shareCodes;
+
+ public DownloadCommand() : base(COMMAND_NAME, @"Download last MM demos of the current Steam account or from share codes")
+ {
+ _shareCodes = new List();
+ _cts = new CancellationTokenSource();
+ _steamService = new SteamService();
+ _demoService = new DemosService(_steamService);
+ BuildDefaultOutputFolderPath();
+ }
+
+ public override void PrintHelp()
+ {
+ Console.WriteLine(GetDescription());
+ Console.WriteLine(@"");
+ Console.WriteLine($@"Usage: {Program.ExeName} {COMMAND_NAME} [shareCodes...] [--output]");
+ Console.WriteLine(@"");
+ Console.WriteLine(@"The --output argument specify the directory where demos will be downloaded.");
+ Console.WriteLine(@"Default to the CSGO ""replays"" folder or the current directory if the ""replays"" folder doesn't exsits.");
+ Console.WriteLine(@"");
+ Console.WriteLine(@"Examples:");
+ Console.WriteLine(@"");
+ Console.WriteLine(@"To download last MM demos of the current Steam account:");
+ Console.WriteLine($@" {Program.ExeName} {COMMAND_NAME}");
+ Console.WriteLine(@"");
+ Console.WriteLine(@"To download demos from share codes:");
+ Console.WriteLine($@" {Program.ExeName} {COMMAND_NAME} CSGO-XXXXX-XXXXX-XXXXX-XXXXX-XXXXX CSGO-XXXXX-XXXXX-XXXXX-XXXXX-XXXXX");
+ Console.WriteLine(@"");
+ Console.WriteLine(@"To change the directory where demos will be downloaded:");
+ Console.WriteLine($@" {Program.ExeName} {COMMAND_NAME} --output ""C:\Users\username\Downloads""");
+ }
+
+ private new void ParseArgs(string[] args)
+ {
+ base.ParseArgs(args);
+
+ for (int index = 0; index < args.Length; index++)
+ {
+ string arg = args[index];
+ bool isOption = arg.StartsWith("--");
+ if (isOption)
+ {
+ switch (arg)
+ {
+ case "--output":
+ if (args.Length > index + 1)
+ {
+ index += 1;
+
+ string directoryPath = Path.GetFullPath(args[index]);
+ bool folderExists = Directory.Exists(directoryPath);
+ if (!folderExists)
+ {
+ Console.WriteLine(@"The output folder doesn't exists");
+ Environment.Exit(1);
+ }
+
+ if (!IsDirectoryWritable(directoryPath))
+ {
+ Console.WriteLine(@"The output folder is not writable");
+ Environment.Exit(1);
+ }
+
+ _outputFolderPath = directoryPath;
+ }
+ else
+ {
+ Console.WriteLine(@"Missing --output argument value");
+ Environment.Exit(1);
+ }
+
+ break;
+ default:
+ Console.WriteLine($@"Unknown option {arg}");
+ Environment.Exit(1);
+ break;
+ }
+ }
+ else
+ {
+ if (_shareCodes.Contains(arg))
+ {
+ continue;
+ }
+
+ try
+ {
+ ShareCode.Decode(arg);
+ _shareCodes.Add(arg);
+ }
+ catch (ShareCode.ShareCodePatternException)
+ {
+ Console.WriteLine($@"Invalid share code {arg}");
+ Environment.Exit(1);
+ }
+ catch (Exception ex)
+ {
+ Console.WriteLine(ex);
+ Environment.Exit(1);
+ }
+ }
+ }
+ }
+
+ public override async Task Run(string[] args)
+ {
+ ParseArgs(args);
+
+ if (_shareCodes.Count > 0)
+ {
+ foreach (string shareCode in _shareCodes)
+ {
+ Console.WriteLine($@"Retrieving match from share code {shareCode}...");
+ try
+ {
+ int exitCode = await _steamService.DownloadDemoFromShareCode(shareCode, _cts.Token);
+ await HandleBoilerExitCode(exitCode);
+ }
+ catch (InvalidBoilerExecutableException)
+ {
+ Console.WriteLine(@"Invalid boiler executable");
+ Environment.Exit(1);
+ }
+ catch (Exception ex)
+ {
+ Console.WriteLine($@"Error while downloading demo from share code {shareCode}: {ex.Message}");
+ Environment.Exit(1);
+ }
+ }
+ }
+ else
+ {
+ try
+ {
+ Console.WriteLine(@"Retrieving recent matches for current Steam account...");
+ int exitCode = await _steamService.GenerateMatchListFile(_cts.Token);
+ await HandleBoilerExitCode(exitCode);
+ }
+ catch (InvalidBoilerExecutableException)
+ {
+ Console.WriteLine(@"Invalid boiler executable");
+ Environment.Exit(1);
+ }
+ catch (Exception ex)
+ {
+ Console.WriteLine($@"Error while downloading demos: {ex.Message}");
+ Environment.Exit(1);
+ }
+ }
+ }
+
+ private async Task HandleBoilerExitCode(int exitCode)
+ {
+ switch (exitCode)
+ {
+ case (int)BoilerExitCode.Success:
+ await DownloadDemos();
+ break;
+ case (int)BoilerExitCode.FatalError:
+ Console.WriteLine($@"Fatal error");
+ break;
+ case (int)BoilerExitCode.InvalidArguments:
+ Console.WriteLine(@"Invalid arguments");
+ break;
+ case (int)BoilerExitCode.CommunicationFailure:
+ Console.WriteLine(@"CSGO Game coordinator communication failure"); ;
+ break;
+ case (int)BoilerExitCode.AlreadyConnectedToGC:
+ Console.WriteLine(@"Already connected to CSGO GC, make sure to close CSGO and retry");
+ break;
+ case (int)BoilerExitCode.SteamRestartRequired:
+ Console.WriteLine(@"Steam needs to be restarted");
+ break;
+ case (int)BoilerExitCode.SteamNotRunningOrLoggedIn:
+ Console.WriteLine(@"Steam is not running or the current account is not logged in");
+ break;
+ case (int)BoilerExitCode.SteamUserNotLoggedIn:
+ Console.WriteLine(@"Current Steam account not connected (maybe offline)");
+ break;
+ case (int)BoilerExitCode.NoMatchesFound:
+ Console.WriteLine(_shareCodes.Count > 0 ? @"Demo link expired" : @"No matches found");
+ break;
+ case (int)BoilerExitCode.WriteFileFailure:
+ Console.WriteLine(@"An error occurred while writing matches file.");
+ break;
+ default:
+ Console.WriteLine($@"Unknown error (code: {exitCode})");
+ break;
+ }
+ }
+
+ private async Task DownloadDemos()
+ {
+ try
+ {
+ _demoService.DownloadFolderPath = _outputFolderPath;
+ Dictionary demoUrlPerDemoName = await _demoService.GetDemoListUrl();
+ if (demoUrlPerDemoName.Count > 0)
+ {
+ Console.WriteLine(@"Downloading demos...");
+ Console.WriteLine($@"Destination folder: {_demoService.DownloadFolderPath}");
+ for (int index = 1; index < demoUrlPerDemoName.Count + 1; index++)
+ {
+ string demoName = demoUrlPerDemoName.ElementAt(index - 1).Key;
+ string demoUrl = demoUrlPerDemoName.ElementAt(index - 1).Value;
+ Console.WriteLine($@"Downloading demo {demoUrl}");
+ await _demoService.DownloadDemo(demoUrl, demoName);
+ Console.WriteLine($@"Extracting demo archive {demoUrl}");
+ await _demoService.DecompressDemoArchive(demoName);
+ }
+
+ Console.WriteLine($@"Demos downloaded in {_demoService.DownloadFolderPath}");
+ }
+ else
+ {
+ Console.WriteLine(@"No matches found or demos already exist in output folder");
+ }
+ }
+ catch (Exception e)
+ {
+ Console.WriteLine(e);
+ }
+ }
+
+ private void BuildDefaultOutputFolderPath()
+ {
+ string csgoFolderPath = AppSettings.GetCsgoPath();
+ string replaysFolderPath = Path.GetFullPath(csgoFolderPath + Path.DirectorySeparatorChar + "replays");
+ if (Directory.Exists(replaysFolderPath))
+ {
+ _outputFolderPath = replaysFolderPath;
+ return;
+ }
+
+ if (IsCurrentDirectoryWritable())
+ {
+ _outputFolderPath = Directory.GetCurrentDirectory();
+ }
+ else
+ {
+ _outputFolderPath = Environment.GetFolderPath(Environment.SpecialFolder.Desktop);
+ }
+ }
+ }
+}
diff --git a/CLI/ExportCommand.cs b/CLI/ExportCommand.cs
new file mode 100644
index 000000000..fd01b787f
--- /dev/null
+++ b/CLI/ExportCommand.cs
@@ -0,0 +1,169 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using Core.Models.Source;
+using System.Linq;
+
+namespace CLI
+{
+ internal abstract class ExportCommand : BaseCommand
+ {
+ protected Source _source;
+ protected readonly List _demoPaths;
+ protected string _outputFolderPath;
+ protected List _availableSources = new List();
+
+ public ExportCommand(string commandName, string description) : base(commandName, description)
+ {
+ _demoPaths = new List();
+ foreach (Source source in Source.Sources)
+ {
+ if (source.Name != Pov.NAME)
+ {
+ _availableSources.Add(source.Name);
+ }
+ }
+ }
+
+ protected void ParseArgs(string[] args, string[] allowedOptions)
+ {
+ base.ParseArgs(args);
+
+ for (int index = 0; index < args.Length; index++)
+ {
+ string arg = args[index];
+ bool isOption = arg.StartsWith("--");
+ if (isOption)
+ {
+ switch (arg)
+ {
+ case "--source":
+ if (args.Length > index + 1)
+ {
+ index += 1;
+ string sourceName = args[index];
+ _source = Source.Factory(sourceName);
+ if (_source == null)
+ {
+ Console.WriteLine($@"Invalid source: {sourceName}");
+ Environment.Exit(1);
+ }
+ }
+ else
+ {
+ Console.WriteLine(@"Missing --source argument value");
+ Environment.Exit(1);
+ }
+
+ break;
+ case "--output":
+ if (args.Length > index + 1)
+ {
+ index += 1;
+ _outputFolderPath = Path.GetFullPath(args[index]).TrimEnd(Path.DirectorySeparatorChar);
+ bool folderExists = Directory.Exists(_outputFolderPath);
+ if (!folderExists)
+ {
+ Console.WriteLine(@"The output folder doesn't exists");
+ Environment.Exit(1);
+ }
+
+ if (!IsDirectoryWritable(_outputFolderPath))
+ {
+ Console.WriteLine(@"The output folder is not writable");
+ Environment.Exit(1);
+ }
+ }
+ else
+ {
+ Console.WriteLine(@"Missing --output argument value");
+ Environment.Exit(1);
+ }
+
+ break;
+ default:
+ if (!allowedOptions.Contains(arg))
+ {
+ Console.WriteLine($@"Unknown option {arg}");
+ Environment.Exit(1);
+ }
+
+ break;
+ }
+ }
+ else
+ {
+ bool isDemoFile = arg.EndsWith(".dem");
+ if (isDemoFile)
+ {
+ bool fileExists = File.Exists(arg);
+ if (!fileExists)
+ {
+ Console.WriteLine($@"The file doesn't exists: {arg}");
+ Environment.Exit(1);
+ }
+
+ if (_demoPaths.Contains(arg))
+ {
+ continue;
+ }
+
+ _demoPaths.Add(arg);
+ }
+ else
+ {
+ try
+ {
+ if (arg.EndsWith("\""))
+ {
+ arg = arg.Substring(0, arg.Length - 1) + "\\";
+ }
+
+ string directoryPath = Path.GetFullPath(arg);
+ bool directoryExists = Directory.Exists(directoryPath);
+ if (directoryExists)
+ {
+ string[] files = Directory.GetFiles(directoryPath, "*.dem");
+ foreach (string file in files)
+ {
+ if (_demoPaths.Contains(file))
+ {
+ continue;
+ }
+
+ _demoPaths.Add(file);
+ }
+ }
+ else
+ {
+ Console.WriteLine($@"The directory doesn't exists: {arg}");
+ Environment.Exit(1);
+ }
+ }
+ catch (Exception ex)
+ {
+ Console.WriteLine($@"Invalid directory: {ex.Message}");
+ Environment.Exit(1);
+ }
+ }
+ }
+ }
+ }
+
+ protected string BuildOutputFolderPathFromDemoPath(string demoPath)
+ {
+ if (!string.IsNullOrEmpty(_outputFolderPath))
+ {
+ return _outputFolderPath;
+ }
+
+ string demoFolderPath = Path.GetDirectoryName(demoPath);
+ if (IsDirectoryWritable(demoFolderPath))
+ {
+ return demoFolderPath;
+ }
+
+ return Environment.GetFolderPath(Environment.SpecialFolder.Desktop);
+ }
+ }
+}
diff --git a/CLI/HelpCommand.cs b/CLI/HelpCommand.cs
new file mode 100644
index 000000000..78d2b61fa
--- /dev/null
+++ b/CLI/HelpCommand.cs
@@ -0,0 +1,37 @@
+using System;
+using System.Threading.Tasks;
+
+namespace CLI
+{
+ internal class HelpCommand : BaseCommand
+ {
+ public const string COMMAND_NAME = "help";
+ private readonly BaseCommand[] _commands;
+
+ public HelpCommand(BaseCommand[] commands) : base(COMMAND_NAME)
+ {
+ _commands = commands;
+ }
+
+ public override Task Run(string[] args)
+ {
+ PrintHelp();
+
+ return Task.FromResult(true);
+ }
+
+ public override void PrintHelp()
+ {
+ Console.WriteLine(@"CSGO Demo Manager CLI");
+ Console.WriteLine(@"");
+ Console.WriteLine($@" Usage: {Program.ExeName} [arguments]");
+ Console.WriteLine(@"");
+ Console.WriteLine(@"The commands are:");
+ Console.WriteLine(@"");
+ foreach (BaseCommand command in _commands)
+ {
+ Console.WriteLine($@" {command.GetName(),-20} {command.GetDescription()}");
+ }
+ }
+ }
+}
diff --git a/CLI/JsonCommand.cs b/CLI/JsonCommand.cs
new file mode 100644
index 000000000..8111605c2
--- /dev/null
+++ b/CLI/JsonCommand.cs
@@ -0,0 +1,88 @@
+using System;
+using System.IO;
+using System.Threading;
+using System.Threading.Tasks;
+using Core.Models;
+using Services.Concrete;
+using Services.Concrete.Analyzer;
+
+namespace CLI
+{
+ internal class JsonCommand : ExportCommand
+ {
+ public const string COMMAND_NAME = "json";
+
+ public JsonCommand() : base(COMMAND_NAME, @"Export demos into JSON files")
+ {
+ }
+
+ public override void PrintHelp()
+ {
+ Console.WriteLine(GetDescription());
+ Console.WriteLine(@"");
+ Console.WriteLine($@"Usage: {Program.ExeName} {COMMAND_NAME} demoPaths... [--output] [--source]");
+ Console.WriteLine(@"");
+ Console.WriteLine(@"Demos path can be either .dem files location or a directory. It can be relative or absolute.");
+ Console.WriteLine(@"The --output argument specify the directory where output files will be saved.");
+ Console.WriteLine($@"The --source argument force the analysis logic of the demo analyzer. Available values: [{string.Join(",", _availableSources)}]");
+ Console.WriteLine(@"");
+ Console.WriteLine(@"Examples:");
+ Console.WriteLine(@"");
+ Console.WriteLine(@"Export a demo:");
+ Console.WriteLine($@" {Program.ExeName} {COMMAND_NAME} ""C:\Users\username\Desktop\demo.dem""");
+ Console.WriteLine(@"");
+ Console.WriteLine(@"Export multiple demos:");
+ Console.WriteLine($@" {Program.ExeName} {COMMAND_NAME} ""C:\Users\username\Desktop\demo.dem"" ""C:\Users\username\Desktop\demo2.dem""");
+ Console.WriteLine(@"");
+ Console.WriteLine(@"Export all demos in a directory using the ESL analyzer and save it in a custom directory:");
+ Console.WriteLine($@" {Program.ExeName} {COMMAND_NAME} ""C:\Users\username\Desktop\MyFolder"" --output ""C:\Users\username\Documents"" --source esl");
+ }
+
+ private new void ParseArgs(string[] args)
+ {
+ base.ParseArgs(args, new string[] { });
+ }
+
+ public override async Task Run(string[] args)
+ {
+ ParseArgs(args);
+
+ if (_demoPaths.Count == 0)
+ {
+ Console.WriteLine(@"No demo paths provided");
+ return;
+ }
+
+ try
+ {
+ foreach (string demoPath in _demoPaths)
+ {
+ Console.WriteLine($@"Analyzing demo {demoPath}");
+ Demo demo = DemoAnalyzer.ParseDemoHeader(demoPath);
+ if (_source != null)
+ {
+ demo.Source = _source;
+ }
+
+ DemoAnalyzer analyzer = DemoAnalyzer.Factory(demo);
+ demo = await analyzer.AnalyzeDemoAsync(new CancellationTokenSource().Token);
+ Console.WriteLine(@"Generating JSON file");
+ string outputFolderPath = BuildOutputFolderPathFromDemoPath(demoPath);
+ CacheService cacheService = new CacheService();
+ string jsonFilePath = await cacheService.GenerateJsonAsync(demo, outputFolderPath);
+ Console.WriteLine($@"JSON file generated at {jsonFilePath}");
+ }
+ }
+ catch (FileNotFoundException ex)
+ {
+ Console.WriteLine($@"The demo doesn't exists: {ex.FileName}");
+ Environment.Exit(1);
+ }
+ catch (Exception ex)
+ {
+ Console.WriteLine($@"Error while exporting demo: {ex.Message}");
+ Environment.Exit(1);
+ }
+ }
+ }
+}
diff --git a/CLI/Program.cs b/CLI/Program.cs
new file mode 100644
index 000000000..9ab1c6a65
--- /dev/null
+++ b/CLI/Program.cs
@@ -0,0 +1,52 @@
+using System;
+using System.Threading.Tasks;
+
+namespace CLI
+{
+ internal class Program
+ {
+ public static string ExeName = AppDomain.CurrentDomain.FriendlyName;
+ private static readonly BaseCommand[] COMMANDS = { new JsonCommand(), new XlsxCommand(), new DownloadCommand() };
+
+ private static async Task Main(string[] args)
+ {
+ string commandName = "";
+ string[] commandArgs = Array.Empty();
+ if (args.Length > 0)
+ {
+ commandName = args[0];
+ commandArgs = new string[args.Length - 1];
+ Array.Copy(args, 1, commandArgs, 0, commandArgs.Length);
+ }
+
+ BaseCommand command = GetCommandToRun(commandName);
+ if (command == null)
+ {
+ Console.WriteLine($@"{commandName}: unknown command");
+ Console.WriteLine($@"Run '{ExeName} help' for usage.");
+ }
+ else
+ {
+ await command.Run(commandArgs);
+ }
+ }
+
+ private static BaseCommand GetCommandToRun(string commandName)
+ {
+ if (commandName == "" || commandName == HelpCommand.COMMAND_NAME || commandName == "--help")
+ {
+ return new HelpCommand(COMMANDS);
+ }
+
+ foreach (BaseCommand command in COMMANDS)
+ {
+ if (command.GetName() == commandName)
+ {
+ return command;
+ }
+ }
+
+ return null;
+ }
+ }
+}
diff --git a/CLI/Properties/AssemblyInfo.cs b/CLI/Properties/AssemblyInfo.cs
new file mode 100644
index 000000000..93a5348b7
--- /dev/null
+++ b/CLI/Properties/AssemblyInfo.cs
@@ -0,0 +1,36 @@
+using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+// General Information about an assembly is controlled through the following
+// set of attributes. Change these attribute values to modify the information
+// associated with an assembly.
+[assembly: AssemblyTitle("CLI")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("")]
+[assembly: AssemblyProduct("CLI")]
+[assembly: AssemblyCopyright("Copyright © 2022")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+// Setting ComVisible to false makes the types in this assembly not visible
+// to COM components. If you need to access a type in this assembly from
+// COM, set the ComVisible attribute to true on that type.
+[assembly: ComVisible(false)]
+
+// The following GUID is for the ID of the typelib if this project is exposed to COM
+[assembly: Guid("86541ef7-d99f-46cc-8836-020d0ae1bce9")]
+
+// Version information for an assembly consists of the following four values:
+//
+// Major Version
+// Minor Version
+// Build Number
+// Revision
+//
+// You can specify all the values or you can default the Build and Revision Numbers
+// by using the '*' as shown below:
+// [assembly: AssemblyVersion("1.0.*")]
+[assembly: AssemblyVersion("1.0.0.0")]
+[assembly: AssemblyFileVersion("1.0.0.0")]
diff --git a/CLI/XlsxCommand.cs b/CLI/XlsxCommand.cs
new file mode 100644
index 000000000..6b1a74732
--- /dev/null
+++ b/CLI/XlsxCommand.cs
@@ -0,0 +1,137 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Threading;
+using System.Threading.Tasks;
+using Core.Models;
+using Services.Concrete.Analyzer;
+using Services.Concrete.Excel;
+
+namespace CLI
+{
+ internal class XlsxCommand : ExportCommand
+ {
+ public const string COMMAND_NAME = "xlsx";
+ private bool _exportIntoSingleFile = false;
+
+ public XlsxCommand() : base(COMMAND_NAME, @"Export demos into XLSX files")
+ {
+ }
+
+ public override void PrintHelp()
+ {
+ Console.WriteLine(GetDescription());
+ Console.WriteLine(@"");
+ Console.WriteLine($@"Usage: {Program.ExeName} {COMMAND_NAME} demoPaths... [--output] [--source] [--single]");
+ Console.WriteLine(@"");
+ Console.WriteLine(@"Demos path can be either .dem files location or a directory. It can be relative or absolute.");
+ Console.WriteLine(@"The --output argument specify the directory where output files will be saved.");
+ Console.WriteLine($@"The --source argument force the analysis logic of the demo analyzer. Available values: [{string.Join(",", _availableSources)}]");
+ Console.WriteLine($@"The --single argument generates a single XLSX file instead of one per demo.");
+ Console.WriteLine(@"");
+ Console.WriteLine(@"Examples:");
+ Console.WriteLine(@"");
+ Console.WriteLine(@"Export a demo:");
+ Console.WriteLine($@" {Program.ExeName} {COMMAND_NAME} ""C:\Users\username\Desktop\demo.dem""");
+ Console.WriteLine(@"");
+ Console.WriteLine(@"Export multiple demos:");
+ Console.WriteLine($@" {Program.ExeName} {COMMAND_NAME} ""C:\Users\username\Desktop\demo.dem"" ""C:\Users\username\Desktop\demo2.dem""");
+ Console.WriteLine(@"");
+ Console.WriteLine(@"Export all demos in a directory using the ESL analyzer and save it in a custom directory:");
+ Console.WriteLine($@" {Program.ExeName} {COMMAND_NAME} ""C:\Users\username\Desktop\MyFolder"" --output ""C:\Users\username\Documents"" --source esl");
+ }
+
+ private new void ParseArgs(string[] args)
+ {
+ ParseArgs(args, new string[] { "--single" });
+ for (int index = 0; index < args.Length; index++)
+ {
+ if (args[index] == "--single")
+ {
+ _exportIntoSingleFile = true;
+ break;
+ }
+ }
+ }
+
+ public override async Task Run(string[] args)
+ {
+ ParseArgs(args);
+
+ if (_demoPaths.Count == 0)
+ {
+ Console.WriteLine(@"No demo paths provided");
+ return;
+ }
+
+ try
+ {
+ ExcelService excelService = new ExcelService();
+ List demos = new List();
+
+ foreach (string demoPath in _demoPaths)
+ {
+ Console.WriteLine($@"Analyzing demo {demoPath}");
+ Demo demo = DemoAnalyzer.ParseDemoHeader(demoPath);
+ if (_source != null)
+ {
+ demo.Source = _source;
+ }
+
+ DemoAnalyzer analyzer = DemoAnalyzer.Factory(demo);
+ demo = await analyzer.AnalyzeDemoAsync(new CancellationTokenSource().Token);
+
+ if (_exportIntoSingleFile)
+ {
+ demos.Add(demo);
+ }
+ else
+ {
+ Console.WriteLine($@"Generating XLSX file");
+ string outputFolderPath = BuildOutputFolderPathFromDemoPath(demoPath);
+ string fileName = outputFolderPath + Path.DirectorySeparatorChar + demo.Name + ".xlsx";
+ await excelService.GenerateXls(demo, fileName);
+ Console.WriteLine($@"XLSX file generated at {fileName}");
+ }
+ }
+
+ if (_exportIntoSingleFile)
+ {
+ if (demos.Count == 0)
+ {
+ Console.WriteLine(@"No demos to export");
+
+ return;
+ }
+
+ if (string.IsNullOrEmpty(_outputFolderPath))
+ {
+ if (IsCurrentDirectoryWritable())
+ {
+ _outputFolderPath = Directory.GetCurrentDirectory();
+ }
+ else
+ {
+ _outputFolderPath = Environment.GetFolderPath(Environment.SpecialFolder.Desktop);
+ }
+ }
+
+ Console.WriteLine(@"Generating XLSX file");
+ string fileName = _outputFolderPath + Path.DirectorySeparatorChar + "export-" + DateTime.Now.ToString("yy-MM-dd-hh-mm-ss") + ".xlsx";
+ await excelService.GenerateXls(demos, fileName);
+ Console.WriteLine($@"XLSX file generated at {fileName}");
+ }
+ }
+ catch (FileNotFoundException ex)
+ {
+ Console.WriteLine($@"The demo doesn't exists: {ex.FileName}");
+ Environment.Exit(1);
+ }
+ catch (Exception ex)
+ {
+ Console.WriteLine($@"Error while exporting demo: {ex.Message}");
+ Environment.Exit(1);
+ }
+ }
+ }
+}
diff --git a/CLI/app.ico b/CLI/app.ico
new file mode 100644
index 000000000..06308003f
Binary files /dev/null and b/CLI/app.ico differ
diff --git a/CLI/packages.config b/CLI/packages.config
new file mode 100644
index 000000000..8c8ddaa10
--- /dev/null
+++ b/CLI/packages.config
@@ -0,0 +1,6 @@
+
+
+
+
+
+
diff --git a/CSGO Demos Manager.sln b/CSGO Demos Manager.sln
index 31e2e4b82..0e9653bfd 100644
--- a/CSGO Demos Manager.sln
+++ b/CSGO Demos Manager.sln
@@ -45,75 +45,162 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution
setup.iss = setup.iss
EndProjectSection
EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CLI", "CLI\CLI.csproj", "{86541EF7-D99F-46CC-8836-020D0AE1BCE9}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Bitstream-Debugging|Any CPU = Bitstream-Debugging|Any CPU
Bitstream-Debugging|x86 = Bitstream-Debugging|x86
+ Debug|Any CPU = Debug|Any CPU
Debug|x86 = Debug|x86
+ Release|Any CPU = Release|Any CPU
Release|x86 = Release|x86
+ SavePropValues|Any CPU = SavePropValues|Any CPU
SavePropValues|x86 = SavePropValues|x86
+ YoloDebug|Any CPU = YoloDebug|Any CPU
YoloDebug|x86 = YoloDebug|x86
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {1D6D9598-4348-4444-A8DF-A634CA0D371B}.Bitstream-Debugging|Any CPU.ActiveCfg = Debug-release|x86
+ {1D6D9598-4348-4444-A8DF-A634CA0D371B}.Bitstream-Debugging|Any CPU.Build.0 = Debug-release|x86
{1D6D9598-4348-4444-A8DF-A634CA0D371B}.Bitstream-Debugging|x86.ActiveCfg = Debug|x86
{1D6D9598-4348-4444-A8DF-A634CA0D371B}.Bitstream-Debugging|x86.Build.0 = Debug|x86
+ {1D6D9598-4348-4444-A8DF-A634CA0D371B}.Debug|Any CPU.ActiveCfg = Debug|x86
+ {1D6D9598-4348-4444-A8DF-A634CA0D371B}.Debug|Any CPU.Build.0 = Debug|x86
{1D6D9598-4348-4444-A8DF-A634CA0D371B}.Debug|x86.ActiveCfg = Debug|x86
{1D6D9598-4348-4444-A8DF-A634CA0D371B}.Debug|x86.Build.0 = Debug|x86
+ {1D6D9598-4348-4444-A8DF-A634CA0D371B}.Release|Any CPU.ActiveCfg = Release|x86
+ {1D6D9598-4348-4444-A8DF-A634CA0D371B}.Release|Any CPU.Build.0 = Release|x86
{1D6D9598-4348-4444-A8DF-A634CA0D371B}.Release|x86.ActiveCfg = Release|x86
{1D6D9598-4348-4444-A8DF-A634CA0D371B}.Release|x86.Build.0 = Release|x86
+ {1D6D9598-4348-4444-A8DF-A634CA0D371B}.SavePropValues|Any CPU.ActiveCfg = Debug-release|x86
+ {1D6D9598-4348-4444-A8DF-A634CA0D371B}.SavePropValues|Any CPU.Build.0 = Debug-release|x86
{1D6D9598-4348-4444-A8DF-A634CA0D371B}.SavePropValues|x86.ActiveCfg = Release|x86
{1D6D9598-4348-4444-A8DF-A634CA0D371B}.SavePropValues|x86.Build.0 = Release|x86
+ {1D6D9598-4348-4444-A8DF-A634CA0D371B}.YoloDebug|Any CPU.ActiveCfg = Debug-release|x86
+ {1D6D9598-4348-4444-A8DF-A634CA0D371B}.YoloDebug|Any CPU.Build.0 = Debug-release|x86
{1D6D9598-4348-4444-A8DF-A634CA0D371B}.YoloDebug|x86.ActiveCfg = Debug|x86
{1D6D9598-4348-4444-A8DF-A634CA0D371B}.YoloDebug|x86.Build.0 = Debug|x86
+ {22D4CFC7-6810-4C18-A353-71A49782394F}.Bitstream-Debugging|Any CPU.ActiveCfg = Bitstream-Debugging|Any CPU
+ {22D4CFC7-6810-4C18-A353-71A49782394F}.Bitstream-Debugging|Any CPU.Build.0 = Bitstream-Debugging|Any CPU
{22D4CFC7-6810-4C18-A353-71A49782394F}.Bitstream-Debugging|x86.ActiveCfg = Bitstream-Debugging|Any CPU
{22D4CFC7-6810-4C18-A353-71A49782394F}.Bitstream-Debugging|x86.Build.0 = Bitstream-Debugging|Any CPU
+ {22D4CFC7-6810-4C18-A353-71A49782394F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {22D4CFC7-6810-4C18-A353-71A49782394F}.Debug|Any CPU.Build.0 = Debug|Any CPU
{22D4CFC7-6810-4C18-A353-71A49782394F}.Debug|x86.ActiveCfg = Debug|Any CPU
{22D4CFC7-6810-4C18-A353-71A49782394F}.Debug|x86.Build.0 = Debug|Any CPU
+ {22D4CFC7-6810-4C18-A353-71A49782394F}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {22D4CFC7-6810-4C18-A353-71A49782394F}.Release|Any CPU.Build.0 = Release|Any CPU
{22D4CFC7-6810-4C18-A353-71A49782394F}.Release|x86.ActiveCfg = Release|Any CPU
{22D4CFC7-6810-4C18-A353-71A49782394F}.Release|x86.Build.0 = Release|Any CPU
+ {22D4CFC7-6810-4C18-A353-71A49782394F}.SavePropValues|Any CPU.ActiveCfg = SavePropValues|Any CPU
+ {22D4CFC7-6810-4C18-A353-71A49782394F}.SavePropValues|Any CPU.Build.0 = SavePropValues|Any CPU
{22D4CFC7-6810-4C18-A353-71A49782394F}.SavePropValues|x86.ActiveCfg = SavePropValues|Any CPU
{22D4CFC7-6810-4C18-A353-71A49782394F}.SavePropValues|x86.Build.0 = SavePropValues|Any CPU
+ {22D4CFC7-6810-4C18-A353-71A49782394F}.YoloDebug|Any CPU.ActiveCfg = YoloDebug|Any CPU
+ {22D4CFC7-6810-4C18-A353-71A49782394F}.YoloDebug|Any CPU.Build.0 = YoloDebug|Any CPU
{22D4CFC7-6810-4C18-A353-71A49782394F}.YoloDebug|x86.ActiveCfg = YoloDebug|Any CPU
{22D4CFC7-6810-4C18-A353-71A49782394F}.YoloDebug|x86.Build.0 = YoloDebug|Any CPU
+ {9E6E1490-43BD-4925-8A3F-84B962828208}.Bitstream-Debugging|Any CPU.ActiveCfg = Debug-release|x86
+ {9E6E1490-43BD-4925-8A3F-84B962828208}.Bitstream-Debugging|Any CPU.Build.0 = Debug-release|x86
{9E6E1490-43BD-4925-8A3F-84B962828208}.Bitstream-Debugging|x86.ActiveCfg = Debug|x86
{9E6E1490-43BD-4925-8A3F-84B962828208}.Bitstream-Debugging|x86.Build.0 = Debug|x86
+ {9E6E1490-43BD-4925-8A3F-84B962828208}.Debug|Any CPU.ActiveCfg = Debug|x86
+ {9E6E1490-43BD-4925-8A3F-84B962828208}.Debug|Any CPU.Build.0 = Debug|x86
{9E6E1490-43BD-4925-8A3F-84B962828208}.Debug|x86.ActiveCfg = Debug|x86
{9E6E1490-43BD-4925-8A3F-84B962828208}.Debug|x86.Build.0 = Debug|x86
+ {9E6E1490-43BD-4925-8A3F-84B962828208}.Release|Any CPU.ActiveCfg = Release|x86
+ {9E6E1490-43BD-4925-8A3F-84B962828208}.Release|Any CPU.Build.0 = Release|x86
{9E6E1490-43BD-4925-8A3F-84B962828208}.Release|x86.ActiveCfg = Release|x86
{9E6E1490-43BD-4925-8A3F-84B962828208}.Release|x86.Build.0 = Release|x86
+ {9E6E1490-43BD-4925-8A3F-84B962828208}.SavePropValues|Any CPU.ActiveCfg = Debug-release|x86
+ {9E6E1490-43BD-4925-8A3F-84B962828208}.SavePropValues|Any CPU.Build.0 = Debug-release|x86
{9E6E1490-43BD-4925-8A3F-84B962828208}.SavePropValues|x86.ActiveCfg = Release|x86
{9E6E1490-43BD-4925-8A3F-84B962828208}.SavePropValues|x86.Build.0 = Release|x86
+ {9E6E1490-43BD-4925-8A3F-84B962828208}.YoloDebug|Any CPU.ActiveCfg = Debug-release|x86
+ {9E6E1490-43BD-4925-8A3F-84B962828208}.YoloDebug|Any CPU.Build.0 = Debug-release|x86
{9E6E1490-43BD-4925-8A3F-84B962828208}.YoloDebug|x86.ActiveCfg = Debug|x86
{9E6E1490-43BD-4925-8A3F-84B962828208}.YoloDebug|x86.Build.0 = Debug|x86
+ {4180E444-4AA1-4564-9296-50323FE504FE}.Bitstream-Debugging|Any CPU.ActiveCfg = Debug-release|x86
+ {4180E444-4AA1-4564-9296-50323FE504FE}.Bitstream-Debugging|Any CPU.Build.0 = Debug-release|x86
{4180E444-4AA1-4564-9296-50323FE504FE}.Bitstream-Debugging|x86.ActiveCfg = Debug|x86
{4180E444-4AA1-4564-9296-50323FE504FE}.Bitstream-Debugging|x86.Build.0 = Debug|x86
+ {4180E444-4AA1-4564-9296-50323FE504FE}.Debug|Any CPU.ActiveCfg = Debug|x86
+ {4180E444-4AA1-4564-9296-50323FE504FE}.Debug|Any CPU.Build.0 = Debug|x86
{4180E444-4AA1-4564-9296-50323FE504FE}.Debug|x86.ActiveCfg = Debug|x86
{4180E444-4AA1-4564-9296-50323FE504FE}.Debug|x86.Build.0 = Debug|x86
+ {4180E444-4AA1-4564-9296-50323FE504FE}.Release|Any CPU.ActiveCfg = Release|x86
+ {4180E444-4AA1-4564-9296-50323FE504FE}.Release|Any CPU.Build.0 = Release|x86
{4180E444-4AA1-4564-9296-50323FE504FE}.Release|x86.ActiveCfg = Release|x86
{4180E444-4AA1-4564-9296-50323FE504FE}.Release|x86.Build.0 = Release|x86
+ {4180E444-4AA1-4564-9296-50323FE504FE}.SavePropValues|Any CPU.ActiveCfg = Debug-release|x86
+ {4180E444-4AA1-4564-9296-50323FE504FE}.SavePropValues|Any CPU.Build.0 = Debug-release|x86
{4180E444-4AA1-4564-9296-50323FE504FE}.SavePropValues|x86.ActiveCfg = Release|x86
{4180E444-4AA1-4564-9296-50323FE504FE}.SavePropValues|x86.Build.0 = Release|x86
+ {4180E444-4AA1-4564-9296-50323FE504FE}.YoloDebug|Any CPU.ActiveCfg = Debug-release|x86
+ {4180E444-4AA1-4564-9296-50323FE504FE}.YoloDebug|Any CPU.Build.0 = Debug-release|x86
{4180E444-4AA1-4564-9296-50323FE504FE}.YoloDebug|x86.ActiveCfg = Debug|x86
{4180E444-4AA1-4564-9296-50323FE504FE}.YoloDebug|x86.Build.0 = Debug|x86
+ {B0B2F0B5-4E7F-41AE-817F-9C0F30DA5367}.Bitstream-Debugging|Any CPU.ActiveCfg = Debug-release|x86
+ {B0B2F0B5-4E7F-41AE-817F-9C0F30DA5367}.Bitstream-Debugging|Any CPU.Build.0 = Debug-release|x86
{B0B2F0B5-4E7F-41AE-817F-9C0F30DA5367}.Bitstream-Debugging|x86.ActiveCfg = Debug|x86
{B0B2F0B5-4E7F-41AE-817F-9C0F30DA5367}.Bitstream-Debugging|x86.Build.0 = Debug|x86
+ {B0B2F0B5-4E7F-41AE-817F-9C0F30DA5367}.Debug|Any CPU.ActiveCfg = Debug|x86
+ {B0B2F0B5-4E7F-41AE-817F-9C0F30DA5367}.Debug|Any CPU.Build.0 = Debug|x86
{B0B2F0B5-4E7F-41AE-817F-9C0F30DA5367}.Debug|x86.ActiveCfg = Debug|x86
{B0B2F0B5-4E7F-41AE-817F-9C0F30DA5367}.Debug|x86.Build.0 = Debug|x86
+ {B0B2F0B5-4E7F-41AE-817F-9C0F30DA5367}.Release|Any CPU.ActiveCfg = Release|x86
+ {B0B2F0B5-4E7F-41AE-817F-9C0F30DA5367}.Release|Any CPU.Build.0 = Release|x86
{B0B2F0B5-4E7F-41AE-817F-9C0F30DA5367}.Release|x86.ActiveCfg = Release|x86
{B0B2F0B5-4E7F-41AE-817F-9C0F30DA5367}.Release|x86.Build.0 = Release|x86
+ {B0B2F0B5-4E7F-41AE-817F-9C0F30DA5367}.SavePropValues|Any CPU.ActiveCfg = Debug-release|x86
+ {B0B2F0B5-4E7F-41AE-817F-9C0F30DA5367}.SavePropValues|Any CPU.Build.0 = Debug-release|x86
{B0B2F0B5-4E7F-41AE-817F-9C0F30DA5367}.SavePropValues|x86.ActiveCfg = Release|x86
{B0B2F0B5-4E7F-41AE-817F-9C0F30DA5367}.SavePropValues|x86.Build.0 = Release|x86
+ {B0B2F0B5-4E7F-41AE-817F-9C0F30DA5367}.YoloDebug|Any CPU.ActiveCfg = Debug-release|x86
+ {B0B2F0B5-4E7F-41AE-817F-9C0F30DA5367}.YoloDebug|Any CPU.Build.0 = Debug-release|x86
{B0B2F0B5-4E7F-41AE-817F-9C0F30DA5367}.YoloDebug|x86.ActiveCfg = Debug|x86
{B0B2F0B5-4E7F-41AE-817F-9C0F30DA5367}.YoloDebug|x86.Build.0 = Debug|x86
+ {496A0714-9ADF-4C56-8E93-3DFA0EBA89D1}.Bitstream-Debugging|Any CPU.ActiveCfg = Debug-release|x86
+ {496A0714-9ADF-4C56-8E93-3DFA0EBA89D1}.Bitstream-Debugging|Any CPU.Build.0 = Debug-release|x86
{496A0714-9ADF-4C56-8E93-3DFA0EBA89D1}.Bitstream-Debugging|x86.ActiveCfg = Debug|x86
{496A0714-9ADF-4C56-8E93-3DFA0EBA89D1}.Bitstream-Debugging|x86.Build.0 = Debug|x86
+ {496A0714-9ADF-4C56-8E93-3DFA0EBA89D1}.Debug|Any CPU.ActiveCfg = Debug|x86
+ {496A0714-9ADF-4C56-8E93-3DFA0EBA89D1}.Debug|Any CPU.Build.0 = Debug|x86
{496A0714-9ADF-4C56-8E93-3DFA0EBA89D1}.Debug|x86.ActiveCfg = Debug|x86
{496A0714-9ADF-4C56-8E93-3DFA0EBA89D1}.Debug|x86.Build.0 = Debug|x86
+ {496A0714-9ADF-4C56-8E93-3DFA0EBA89D1}.Release|Any CPU.ActiveCfg = Release|x86
+ {496A0714-9ADF-4C56-8E93-3DFA0EBA89D1}.Release|Any CPU.Build.0 = Release|x86
{496A0714-9ADF-4C56-8E93-3DFA0EBA89D1}.Release|x86.ActiveCfg = Release|x86
{496A0714-9ADF-4C56-8E93-3DFA0EBA89D1}.Release|x86.Build.0 = Release|x86
+ {496A0714-9ADF-4C56-8E93-3DFA0EBA89D1}.SavePropValues|Any CPU.ActiveCfg = Debug-release|x86
+ {496A0714-9ADF-4C56-8E93-3DFA0EBA89D1}.SavePropValues|Any CPU.Build.0 = Debug-release|x86
{496A0714-9ADF-4C56-8E93-3DFA0EBA89D1}.SavePropValues|x86.ActiveCfg = Release|x86
{496A0714-9ADF-4C56-8E93-3DFA0EBA89D1}.SavePropValues|x86.Build.0 = Release|x86
+ {496A0714-9ADF-4C56-8E93-3DFA0EBA89D1}.YoloDebug|Any CPU.ActiveCfg = Debug-release|x86
+ {496A0714-9ADF-4C56-8E93-3DFA0EBA89D1}.YoloDebug|Any CPU.Build.0 = Debug-release|x86
{496A0714-9ADF-4C56-8E93-3DFA0EBA89D1}.YoloDebug|x86.ActiveCfg = Debug|x86
{496A0714-9ADF-4C56-8E93-3DFA0EBA89D1}.YoloDebug|x86.Build.0 = Debug|x86
+ {86541EF7-D99F-46CC-8836-020D0AE1BCE9}.Bitstream-Debugging|Any CPU.ActiveCfg = Debug|Any CPU
+ {86541EF7-D99F-46CC-8836-020D0AE1BCE9}.Bitstream-Debugging|Any CPU.Build.0 = Debug|Any CPU
+ {86541EF7-D99F-46CC-8836-020D0AE1BCE9}.Bitstream-Debugging|x86.ActiveCfg = Debug|Any CPU
+ {86541EF7-D99F-46CC-8836-020D0AE1BCE9}.Bitstream-Debugging|x86.Build.0 = Debug|Any CPU
+ {86541EF7-D99F-46CC-8836-020D0AE1BCE9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {86541EF7-D99F-46CC-8836-020D0AE1BCE9}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {86541EF7-D99F-46CC-8836-020D0AE1BCE9}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {86541EF7-D99F-46CC-8836-020D0AE1BCE9}.Debug|x86.Build.0 = Debug|Any CPU
+ {86541EF7-D99F-46CC-8836-020D0AE1BCE9}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {86541EF7-D99F-46CC-8836-020D0AE1BCE9}.Release|Any CPU.Build.0 = Release|Any CPU
+ {86541EF7-D99F-46CC-8836-020D0AE1BCE9}.Release|x86.ActiveCfg = Release|Any CPU
+ {86541EF7-D99F-46CC-8836-020D0AE1BCE9}.Release|x86.Build.0 = Release|Any CPU
+ {86541EF7-D99F-46CC-8836-020D0AE1BCE9}.SavePropValues|Any CPU.ActiveCfg = Debug|Any CPU
+ {86541EF7-D99F-46CC-8836-020D0AE1BCE9}.SavePropValues|Any CPU.Build.0 = Debug|Any CPU
+ {86541EF7-D99F-46CC-8836-020D0AE1BCE9}.SavePropValues|x86.ActiveCfg = Debug|Any CPU
+ {86541EF7-D99F-46CC-8836-020D0AE1BCE9}.SavePropValues|x86.Build.0 = Debug|Any CPU
+ {86541EF7-D99F-46CC-8836-020D0AE1BCE9}.YoloDebug|Any CPU.ActiveCfg = Debug|Any CPU
+ {86541EF7-D99F-46CC-8836-020D0AE1BCE9}.YoloDebug|Any CPU.Build.0 = Debug|Any CPU
+ {86541EF7-D99F-46CC-8836-020D0AE1BCE9}.YoloDebug|x86.ActiveCfg = Debug|Any CPU
+ {86541EF7-D99F-46CC-8836-020D0AE1BCE9}.YoloDebug|x86.Build.0 = Debug|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
diff --git a/Core/MultilingualResources/Core.ar.xlf b/Core/MultilingualResources/Core.ar.xlf
index 049704e74..22c5e3cc8 100644
--- a/Core/MultilingualResources/Core.ar.xlf
+++ b/Core/MultilingualResources/Core.ar.xlf
@@ -202,6 +202,10 @@
..\Resources\Images\Logos\wanmei.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
+
+
+ ..\Resources\Images\Logos\esportal.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
+