Compare commits
6 commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8284fdc1a0 | ||
|
|
da10ecb690 | ||
|
|
5926190a0f | ||
|
|
9507a43164 | ||
|
|
e1ae2e1ec4 | ||
|
|
16ac42dffc |
9 changed files with 1161 additions and 814 deletions
4
.editorconfig
Normal file
4
.editorconfig
Normal file
|
|
@ -0,0 +1,4 @@
|
||||||
|
[*.cs]
|
||||||
|
|
||||||
|
# CA1822: Member als statisch markieren
|
||||||
|
dotnet_diagnostic.CA1822.severity = none
|
||||||
21
ExeToBat.sln
21
ExeToBat.sln
|
|
@ -1,9 +1,14 @@
|
||||||
|
|
||||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||||
# Visual Studio 15
|
# Visual Studio Version 17
|
||||||
VisualStudioVersion = 15.0.28010.2036
|
VisualStudioVersion = 17.3.32929.385
|
||||||
MinimumVisualStudioVersion = 10.0.40219.1
|
MinimumVisualStudioVersion = 10.0.40219.1
|
||||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ExeToBat", "ExeToBat\ExeToBat.csproj", "{04F45237-23C8-4EE6-B61C-6C47B9979A4B}"
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ExeToBat", "ExeToBat\ExeToBat.csproj", "{97ED5369-CCC1-4EAD-B316-97FB983E0A62}"
|
||||||
|
EndProject
|
||||||
|
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{1E4294A3-12F4-4F45-93B3-05D31958042B}"
|
||||||
|
ProjectSection(SolutionItems) = preProject
|
||||||
|
.editorconfig = .editorconfig
|
||||||
|
EndProjectSection
|
||||||
EndProject
|
EndProject
|
||||||
Global
|
Global
|
||||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
|
|
@ -11,15 +16,15 @@ Global
|
||||||
Release|Any CPU = Release|Any CPU
|
Release|Any CPU = Release|Any CPU
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||||
{04F45237-23C8-4EE6-B61C-6C47B9979A4B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
{97ED5369-CCC1-4EAD-B316-97FB983E0A62}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
{04F45237-23C8-4EE6-B61C-6C47B9979A4B}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
{97ED5369-CCC1-4EAD-B316-97FB983E0A62}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
{04F45237-23C8-4EE6-B61C-6C47B9979A4B}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
{97ED5369-CCC1-4EAD-B316-97FB983E0A62}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
{04F45237-23C8-4EE6-B61C-6C47B9979A4B}.Release|Any CPU.Build.0 = Release|Any CPU
|
{97ED5369-CCC1-4EAD-B316-97FB983E0A62}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
GlobalSection(SolutionProperties) = preSolution
|
GlobalSection(SolutionProperties) = preSolution
|
||||||
HideSolutionNode = FALSE
|
HideSolutionNode = FALSE
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||||
SolutionGuid = {4AA99DAC-6B37-42AF-8FB7-8394329BD79A}
|
SolutionGuid = {9F9811F2-7CC0-4D74-8D4A-51D126D13ECD}
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
EndGlobal
|
EndGlobal
|
||||||
|
|
|
||||||
|
|
@ -1,6 +0,0 @@
|
||||||
<?xml version="1.0" encoding="utf-8" ?>
|
|
||||||
<configuration>
|
|
||||||
<startup>
|
|
||||||
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.6.1" />
|
|
||||||
</startup>
|
|
||||||
</configuration>
|
|
||||||
|
|
@ -1,720 +0,0 @@
|
||||||
using System;
|
|
||||||
using System.IO;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
|
|
||||||
namespace ExeToBat
|
|
||||||
{
|
|
||||||
static class Generator
|
|
||||||
{
|
|
||||||
public const int chunk = 8000;
|
|
||||||
|
|
||||||
public static List<SourceFile> Sources = new List<SourceFile>();
|
|
||||||
|
|
||||||
static void Main() => MainMenu();
|
|
||||||
|
|
||||||
static void MainMenu()
|
|
||||||
{
|
|
||||||
Dictionary<string, Action> options = new Dictionary<string, Action> {
|
|
||||||
{ "Files", ChooseSource },
|
|
||||||
{ "Generate", () => BuildBat(Sources, "output.bat") }
|
|
||||||
};
|
|
||||||
|
|
||||||
new ListMenu<string>(options.Keys.ToList())
|
|
||||||
{
|
|
||||||
DisplayTitle = (List<string> Options) => Console.WriteLine("ExeToBat > Main"),
|
|
||||||
DisplayEntry = (List<string> Options, int index, int i) =>
|
|
||||||
{
|
|
||||||
Console.WriteLine("[{0}] {1}", i, Options[index]);
|
|
||||||
},
|
|
||||||
HandleEntry = (List<string> Options, int index) =>
|
|
||||||
{
|
|
||||||
if (options.ContainsKey(Options[index]))
|
|
||||||
{
|
|
||||||
options[Options[index]]();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
ResetInput();
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
},
|
|
||||||
ExitEntry = "Exit",
|
|
||||||
|
|
||||||
}.Show();
|
|
||||||
}
|
|
||||||
|
|
||||||
static void ChooseSource()
|
|
||||||
{
|
|
||||||
new ListMenu<SourceFile>(Sources)
|
|
||||||
{
|
|
||||||
DisplayTitle = (List<SourceFile> sources) =>
|
|
||||||
{
|
|
||||||
Console.WriteLine("ExeToBat > Main > Files");
|
|
||||||
Console.WriteLine("[{0}] ({1})", Convert.ToString(0).PadLeft(Convert.ToString(sources.Count).Length, ' '), "Add Files");
|
|
||||||
},
|
|
||||||
DisplayEntry = (List<SourceFile> sources, int index, int i) =>
|
|
||||||
Console.WriteLine("[{0}] {1}",
|
|
||||||
Convert.ToString(i).PadLeft(
|
|
||||||
Convert.ToString(sources.Count).Length, ' '),
|
|
||||||
Path.GetFileName(sources[index].File)),
|
|
||||||
ZeroEntry = (List<SourceFile> sources) =>
|
|
||||||
{
|
|
||||||
AddSource();
|
|
||||||
return false;
|
|
||||||
},
|
|
||||||
HandleEntry = (List<SourceFile> sources, int index) =>
|
|
||||||
{
|
|
||||||
ManageSource(sources[index]);
|
|
||||||
return false;
|
|
||||||
},
|
|
||||||
RefreshEntries = (List<SourceFile> sources) => Sources,
|
|
||||||
|
|
||||||
}.Show();
|
|
||||||
}
|
|
||||||
|
|
||||||
static void AddSource()
|
|
||||||
{
|
|
||||||
bool IsInputValid = false;
|
|
||||||
while (!IsInputValid)
|
|
||||||
{
|
|
||||||
|
|
||||||
Console.Clear();
|
|
||||||
Console.WriteLine("ExeToBat > Main > Files > Add");
|
|
||||||
Console.Write("\n");
|
|
||||||
Console.Write("{0}> ", "File/Folder");
|
|
||||||
string input = Console.ReadLine();
|
|
||||||
|
|
||||||
input.Trim();
|
|
||||||
input = input.Replace("\"", "");
|
|
||||||
if (!string.IsNullOrEmpty(input))
|
|
||||||
{
|
|
||||||
switch (input)
|
|
||||||
{
|
|
||||||
case var i when Directory.Exists(i):
|
|
||||||
IsInputValid = true;
|
|
||||||
foreach (string file in Directory.GetFiles(input))
|
|
||||||
{
|
|
||||||
Sources.Add(new SourceFile(file));
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case var i when File.Exists(i):
|
|
||||||
IsInputValid = true;
|
|
||||||
Sources.Add(new SourceFile(input));
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
ResetInput();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
IsInputValid = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void ManageSource(SourceFile source)
|
|
||||||
{
|
|
||||||
Dictionary<string, Func<bool>> options = new Dictionary<string, Func<bool>>
|
|
||||||
{
|
|
||||||
{"Edit", () => {
|
|
||||||
ModifySource(source);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{ "Position", () => {
|
|
||||||
EditPosition(source);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{ "Delete", () => {
|
|
||||||
Sources.Remove(source);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
new ListMenu<string>(options.Keys.ToList())
|
|
||||||
{
|
|
||||||
DisplayTitle = (List<string> Options) => Console.WriteLine("ExeToBat > Main > Files > {0}", Path.GetFileName(source.File)),
|
|
||||||
DisplayEntry = (List<string> Options, int index, int i) => Console.WriteLine("[{0}] {1}", i, Options[index]),
|
|
||||||
HandleEntry = (List<string> Options, int index) =>
|
|
||||||
{
|
|
||||||
if (options.ContainsKey(Options[index]))
|
|
||||||
{
|
|
||||||
return options[Options[index]]();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
ResetInput();
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
},
|
|
||||||
|
|
||||||
}.Show();
|
|
||||||
}
|
|
||||||
|
|
||||||
static void ModifySource(SourceFile source)
|
|
||||||
{
|
|
||||||
|
|
||||||
// this could be solved better with an enum
|
|
||||||
|
|
||||||
Dictionary<string, Action> options()
|
|
||||||
{
|
|
||||||
Dictionary<string, Action> opts = new Dictionary<string, Action> {
|
|
||||||
{"File", () => { } },
|
|
||||||
{"Extraction directory", () => EditExtraction(source)},
|
|
||||||
{"Execute after extraction", () => source.Execute = !source.Execute },
|
|
||||||
};
|
|
||||||
if (source.Execute)
|
|
||||||
{
|
|
||||||
opts.Add("Parameters", () => EditParameters(source));
|
|
||||||
opts.Add("Wait for exit", () => source.Wait = !source.Wait);
|
|
||||||
}
|
|
||||||
if (source.Execute && source.Wait) { opts.Add("Delete after execution", () => source.Delete = !source.Delete); }
|
|
||||||
return opts;
|
|
||||||
}
|
|
||||||
|
|
||||||
Dictionary<string, string> ValueMap = new Dictionary<string, string>
|
|
||||||
{
|
|
||||||
{ "File", "File" },
|
|
||||||
{ "Extraction directory", "Directory" },
|
|
||||||
{ "Execute after extraction", "Execute" },
|
|
||||||
{ "Parameters", "Parameters" },
|
|
||||||
{ "Wait for exit", "Wait"},
|
|
||||||
{ "Delete after execution", "Delete"},
|
|
||||||
};
|
|
||||||
|
|
||||||
new ListMenu<string>(options().Keys.ToList())
|
|
||||||
{
|
|
||||||
DisplayTitle = (List<string> Options) => Console.WriteLine("ExeToBat > Main > Files > {0} > Edit", Path.GetFileName(source.File)),
|
|
||||||
DisplayEntry = (List<string> Options, int index, int i) =>
|
|
||||||
{
|
|
||||||
int MaxLength = options().Keys.Select(x => x.Length).Max();
|
|
||||||
Console.WriteLine("[{0}] {1} | {2}", i, Options[index].PadRight(MaxLength, ' '), source.GetType().GetProperty(ValueMap[Options[index]]).GetValue(source).ToString());
|
|
||||||
},
|
|
||||||
HandleEntry = (List<string> Options, int index) =>
|
|
||||||
{
|
|
||||||
if (options().ContainsKey(Options[index]))
|
|
||||||
{
|
|
||||||
options()[Options[index]]();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
ResetInput();
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
},
|
|
||||||
RefreshEntries = (List<string> Options) => options().Keys.ToList(),
|
|
||||||
|
|
||||||
}.Show();
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
static void EditExtraction(SourceFile source)
|
|
||||||
{
|
|
||||||
bool IsInputValid = false;
|
|
||||||
while (!IsInputValid)
|
|
||||||
{
|
|
||||||
Console.Clear();
|
|
||||||
Console.WriteLine("ExeToBat > Main > Files > {0} > Edit > Extraction", Path.GetFileName(source.File));
|
|
||||||
Console.Write("\n");
|
|
||||||
Console.WriteLine("Documentation: ");
|
|
||||||
Console.WriteLine("https://ss64.com/nt/syntax-variables.html");
|
|
||||||
Console.WriteLine("https://ss64.com/nt/syntax-args.html");
|
|
||||||
Console.Write("\n");
|
|
||||||
Console.Write("{0}> ", "Directory");
|
|
||||||
string input = Console.ReadLine();
|
|
||||||
|
|
||||||
input.Trim();
|
|
||||||
if (!string.IsNullOrEmpty(input))
|
|
||||||
{
|
|
||||||
source.Directory = input;
|
|
||||||
IsInputValid = true;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
IsInputValid = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void EditParameters(SourceFile source)
|
|
||||||
{
|
|
||||||
bool IsInputValid = false;
|
|
||||||
while (!IsInputValid)
|
|
||||||
{
|
|
||||||
Console.Clear();
|
|
||||||
Console.WriteLine("ExeToBat > Main > Files > {0} > Edit > Parameters", Path.GetFileName(source.File));
|
|
||||||
Console.Write("\n");
|
|
||||||
Console.Write("{0}> ", "Parameters");
|
|
||||||
string input = Console.ReadLine();
|
|
||||||
|
|
||||||
input.Trim();
|
|
||||||
if (!string.IsNullOrEmpty(input))
|
|
||||||
{
|
|
||||||
source.Parameters = input;
|
|
||||||
IsInputValid = true;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
IsInputValid = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void EditPosition(SourceFile source)
|
|
||||||
{
|
|
||||||
bool IsInputValid = false;
|
|
||||||
while (!IsInputValid)
|
|
||||||
{
|
|
||||||
Console.Clear();
|
|
||||||
Console.WriteLine("ExeToBat > Main > Files > {0} > Position : {1}", Path.GetFileName(source.File), Sources.IndexOf(source));
|
|
||||||
|
|
||||||
Console.Write("\n");
|
|
||||||
Console.Write("{0}> ", "New index");
|
|
||||||
string input = Console.ReadLine();
|
|
||||||
|
|
||||||
if (int.TryParse(input, out int index))
|
|
||||||
{
|
|
||||||
if (index < Sources.Count)
|
|
||||||
{
|
|
||||||
Sources.Remove(source);
|
|
||||||
Sources.Insert(index, source);
|
|
||||||
IsInputValid = true;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
ResetInput();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (string.IsNullOrEmpty(input))
|
|
||||||
{
|
|
||||||
IsInputValid = true;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
ResetInput();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void BuildBat(List<SourceFile> sources, string outputFile)
|
|
||||||
{
|
|
||||||
Console.Clear();
|
|
||||||
Console.WriteLine("ExeToBat > Main > Generate");
|
|
||||||
|
|
||||||
if (Sources.Any())
|
|
||||||
{
|
|
||||||
using (StreamWriter writer = new StreamWriter(outputFile))
|
|
||||||
{
|
|
||||||
Console.WriteLine("[Preparing] basic batch structure...");
|
|
||||||
writer.WriteLine("@echo off");
|
|
||||||
writer.WriteLine(":: Auto-generated batch file by ExeToBat ::");
|
|
||||||
writer.WriteLine("");
|
|
||||||
|
|
||||||
foreach (SourceFile source in sources)
|
|
||||||
{
|
|
||||||
Console.WriteLine("[ Reading ] {0}", source.File);
|
|
||||||
List<string> fileChunks = Convert.ToBase64String(File.ReadAllBytes(source.File)).Chunks(chunk).ToList();
|
|
||||||
string tempFile = Path.Combine("%temp%", source.Resource);
|
|
||||||
writer.WriteLine("(");
|
|
||||||
|
|
||||||
int pos = 0;
|
|
||||||
foreach (string part in fileChunks)
|
|
||||||
{
|
|
||||||
pos++;
|
|
||||||
Console.Write("[ Writing ] {0} part {1}/{2}\r", Path.GetFileName(source.File), pos.ToString().PadLeft(fileChunks.Count.ToString().Length, '0'), fileChunks.Count);
|
|
||||||
writer.WriteLine(string.Format("echo {0}", part));
|
|
||||||
}
|
|
||||||
|
|
||||||
Console.WriteLine();
|
|
||||||
writer.WriteLine(string.Format(") >> \"{0}\"", tempFile));
|
|
||||||
writer.WriteLine("");
|
|
||||||
|
|
||||||
Console.WriteLine("[ Writing ] decode mechanism");
|
|
||||||
writer.WriteLine(string.Format("certutil -decode \"{0}\" \"{1}\" >nul 2>&1", tempFile, Path.Combine(source.Directory, Path.GetFileName(source.File))));
|
|
||||||
writer.WriteLine(string.Format("del /f /q \"{0}\" >nul 2>&1", tempFile));
|
|
||||||
writer.WriteLine("");
|
|
||||||
|
|
||||||
if (source.Execute)
|
|
||||||
{
|
|
||||||
string wait;
|
|
||||||
if (source.Wait) { wait = " /wait"; } else { wait = " "; }
|
|
||||||
Console.WriteLine("[ Writing ] execute mechanism");
|
|
||||||
writer.WriteLine(string.Format("start{0} \"\" \"cmd /c {1}\" {2}", wait, Path.Combine(source.Directory, Path.GetFileName(source.File)), source.Parameters));
|
|
||||||
if (source.Wait)
|
|
||||||
{
|
|
||||||
Console.WriteLine("[ Writing ] wait mechanism");
|
|
||||||
if (source.Delete)
|
|
||||||
{
|
|
||||||
Console.WriteLine("[ Writing ] delete mechanism");
|
|
||||||
writer.WriteLine(string.Format("del /f /q \"{0}\" >nul 2>&1", Path.Combine(source.Directory, Path.GetFileName(source.File))));
|
|
||||||
writer.WriteLine("");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
writer.WriteLine("");
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
writer.Flush();
|
|
||||||
Console.WriteLine("[Generated] {0}", Path.GetFileName(source.File));
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
Console.WriteLine("[Generated] All done");
|
|
||||||
|
|
||||||
Console.WriteLine("Press anything...");
|
|
||||||
Console.ReadKey();
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Console.WriteLine("No files specified");
|
|
||||||
Wait(500);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public class SourceFile
|
|
||||||
{
|
|
||||||
public string File { get; set; }
|
|
||||||
public bool Execute { get; set; } = false;
|
|
||||||
public bool Wait { get; set; } = false;
|
|
||||||
public bool Delete { get; set; } = false;
|
|
||||||
public string Resource { get; set; } = GenTemp();
|
|
||||||
public string Directory { get; set; } = "%~dp0";
|
|
||||||
public string Parameters { get; set; } = "";
|
|
||||||
|
|
||||||
|
|
||||||
public SourceFile(string file)
|
|
||||||
{
|
|
||||||
File = file;
|
|
||||||
}
|
|
||||||
|
|
||||||
static public string GenTemp()
|
|
||||||
{
|
|
||||||
return string.Format($"res_{new Random().Next(1000, 10000)}.b64");
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
static string[] Chunks(this string toSplit, int chunkSize)
|
|
||||||
{
|
|
||||||
int stringLength = toSplit.Length;
|
|
||||||
|
|
||||||
int chunksRequired = (int)Math.Ceiling(stringLength / (decimal)chunkSize);
|
|
||||||
var stringArray = new string[chunksRequired];
|
|
||||||
|
|
||||||
int lengthRemaining = stringLength;
|
|
||||||
|
|
||||||
for (int i = 0; i < chunksRequired; i++)
|
|
||||||
{
|
|
||||||
int lengthToUse = Math.Min(lengthRemaining, chunkSize);
|
|
||||||
int startIndex = chunkSize * i;
|
|
||||||
stringArray[i] = toSplit.Substring(startIndex, lengthToUse);
|
|
||||||
|
|
||||||
lengthRemaining -= lengthToUse;
|
|
||||||
}
|
|
||||||
|
|
||||||
return stringArray;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
public static void Wait(int ms)
|
|
||||||
{
|
|
||||||
using (System.Threading.ManualResetEvent wait = new System.Threading.ManualResetEvent(false))
|
|
||||||
{
|
|
||||||
wait.WaitOne(ms);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public class ListMenu<T>
|
|
||||||
{
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// CLOE (Command-line List Options Enumerator).
|
|
||||||
/// <para>Turns a List<T> into a menu of options. Each list item is asigned a number. Provides several callbacks to customise the menu.</para>
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="entries">List of objects you want to display</param>
|
|
||||||
public ListMenu(List<T> entries)
|
|
||||||
{
|
|
||||||
Entries = entries;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The prompt that is displayed to the user.
|
|
||||||
/// </summary>
|
|
||||||
public string Prompt = "Choose";
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The string to be displayed for the option to exit the menu.
|
|
||||||
/// </summary>
|
|
||||||
public string ExitEntry = "Back";
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The key the user has to press to exit the menu.
|
|
||||||
/// </summary>
|
|
||||||
public char ExitKey = 'q';
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Wether or not the user can exit the menu.
|
|
||||||
/// </summary>
|
|
||||||
public bool UserCanExit = true;
|
|
||||||
|
|
||||||
|
|
||||||
private List<T> Entries;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The function that processes the chosen menu entries.
|
|
||||||
/// </summary>
|
|
||||||
public Func<List<T>, int, bool> HandleEntry = (entries, index) =>
|
|
||||||
{
|
|
||||||
Console.Clear();
|
|
||||||
Console.WriteLine(entries[index]);
|
|
||||||
Wait(200);
|
|
||||||
return false;
|
|
||||||
};
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The function that displays the menu title.
|
|
||||||
/// </summary>
|
|
||||||
public Action<List<T>> DisplayTitle = (entries) => { };
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The function that displays the entry to the user.
|
|
||||||
/// </summary>
|
|
||||||
public Action<List<T>, int, int> DisplayEntry = (entries, index, num) =>
|
|
||||||
{
|
|
||||||
Console.WriteLine("[{0}] {1}", Convert.ToString(num).PadLeft(Convert.ToString(entries.Count).Length, ' '), entries[index]);
|
|
||||||
};
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The function to update the list of entries.
|
|
||||||
/// </summary>
|
|
||||||
public Func<List<T>, List<T>> RefreshEntries = (entries) =>
|
|
||||||
{
|
|
||||||
return entries;
|
|
||||||
};
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The function that is called when 0th entry in the list is chosen.
|
|
||||||
/// <para>Display this entry with the title function.</para>
|
|
||||||
/// </summary>
|
|
||||||
public Func<List<T>, bool> ZeroEntry = (entries) =>
|
|
||||||
{
|
|
||||||
ResetInput();
|
|
||||||
return false;
|
|
||||||
};
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Display the menu.
|
|
||||||
/// </summary>
|
|
||||||
/// <returns></returns>
|
|
||||||
public ListMenu<T> Show()
|
|
||||||
{
|
|
||||||
string readInput = string.Empty;
|
|
||||||
bool MenuExitIsPending = false;
|
|
||||||
while (!MenuExitIsPending)
|
|
||||||
{
|
|
||||||
Console.Clear();
|
|
||||||
int printedEntries = 0;
|
|
||||||
Entries = RefreshEntries(Entries);
|
|
||||||
DisplayTitle(Entries);
|
|
||||||
if (Entries.Any())
|
|
||||||
{
|
|
||||||
int num = 0;
|
|
||||||
foreach (T entry in Entries)
|
|
||||||
{
|
|
||||||
num++;
|
|
||||||
if (string.IsNullOrEmpty(readInput) || Convert.ToString(num).StartsWith(readInput))
|
|
||||||
{
|
|
||||||
DisplayEntry(Entries, Entries.IndexOf(entry), num);
|
|
||||||
printedEntries++;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Entries.Count > Console.WindowHeight - 5)
|
|
||||||
{
|
|
||||||
if (printedEntries >= Console.WindowHeight - (5 + 1))
|
|
||||||
{
|
|
||||||
Console.WriteLine("[{0}] +{1}", ".".PadLeft(Convert.ToString(Entries.Count).Length, '.'), Entries.Count);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (printedEntries == Console.WindowHeight - 5)
|
|
||||||
{
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (UserCanExit)
|
|
||||||
{
|
|
||||||
Console.WriteLine("[{0}] {1}", Convert.ToString(ExitKey).PadLeft(Convert.ToString(Entries.Count).Length, ' '), ExitEntry);
|
|
||||||
}
|
|
||||||
|
|
||||||
Console.WriteLine();
|
|
||||||
|
|
||||||
bool InputIsValid = false;
|
|
||||||
while (!InputIsValid)
|
|
||||||
{
|
|
||||||
Console.Write("{0}> {1}", Prompt, readInput);
|
|
||||||
ConsoleKeyInfo input = Console.ReadKey();
|
|
||||||
Wait(20);
|
|
||||||
int choiceNum = -1;
|
|
||||||
switch (input)
|
|
||||||
{
|
|
||||||
case var key when key.KeyChar.Equals(ExitKey):
|
|
||||||
if (UserCanExit)
|
|
||||||
{
|
|
||||||
Console.WriteLine();
|
|
||||||
InputIsValid = true;
|
|
||||||
MenuExitIsPending = true;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Console.WriteLine();
|
|
||||||
ResetInput();
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case var key when key.Key.Equals(ConsoleKey.Backspace):
|
|
||||||
if (!string.IsNullOrEmpty(readInput))
|
|
||||||
{
|
|
||||||
Console.Write("\b");
|
|
||||||
readInput = readInput.Remove(readInput.Length - 1);
|
|
||||||
}
|
|
||||||
InputIsValid = true;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case var key when key.Key.Equals(ConsoleKey.Enter):
|
|
||||||
if (!string.IsNullOrEmpty(readInput))
|
|
||||||
{
|
|
||||||
if (HandleEntry(Entries, (Convert.ToInt32(readInput) - 1)))
|
|
||||||
{
|
|
||||||
MenuExitIsPending = true;
|
|
||||||
}
|
|
||||||
readInput = string.Empty;
|
|
||||||
}
|
|
||||||
InputIsValid = true;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case var key when int.TryParse(key.KeyChar.ToString(), out choiceNum):
|
|
||||||
Console.WriteLine();
|
|
||||||
if (string.IsNullOrEmpty(readInput) && choiceNum.Equals(0))
|
|
||||||
{
|
|
||||||
InputIsValid = true;
|
|
||||||
if (ZeroEntry(Entries))
|
|
||||||
{
|
|
||||||
MenuExitIsPending = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (Convert.ToInt32(readInput + Convert.ToString(choiceNum)) <= Entries.Count)
|
|
||||||
{
|
|
||||||
InputIsValid = true;
|
|
||||||
int matchingEntries = 0;
|
|
||||||
readInput = new System.Text.StringBuilder().Append(readInput).Append(Convert.ToString(choiceNum)).ToString();
|
|
||||||
for (int i = 0; i < Entries.Count; i++)
|
|
||||||
{
|
|
||||||
if (Convert.ToString(i + 1).StartsWith(readInput) || Convert.ToString(i + 1) == readInput) { matchingEntries++; }
|
|
||||||
}
|
|
||||||
if ((readInput.Length == Convert.ToString(Entries.Count).Length) || (matchingEntries == 1))
|
|
||||||
{
|
|
||||||
if (HandleEntry(Entries, (Convert.ToInt32(readInput) - 1)))
|
|
||||||
{
|
|
||||||
MenuExitIsPending = true;
|
|
||||||
}
|
|
||||||
readInput = string.Empty;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
ResetInput();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
Console.WriteLine();
|
|
||||||
ResetInput();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// A simple template to create Yes or No menus.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="title">The title of the menu.</param>
|
|
||||||
/// <param name="Yes">The function to be called upon Yes</param>
|
|
||||||
/// <param name="No">The function to be called upon No</param>
|
|
||||||
public static void YesNoMenu(string title, Action Yes, Action No)
|
|
||||||
{
|
|
||||||
bool IsInputValid = false;
|
|
||||||
while (!IsInputValid)
|
|
||||||
{
|
|
||||||
Console.Write("{0}? [{1}]> ", title, "Y/N");
|
|
||||||
string Input = Console.ReadKey().KeyChar.ToString();
|
|
||||||
Wait(20);
|
|
||||||
Console.Write("\n");
|
|
||||||
if (string.Equals(Input, "Y", StringComparison.OrdinalIgnoreCase))
|
|
||||||
{
|
|
||||||
IsInputValid = true;
|
|
||||||
Yes();
|
|
||||||
|
|
||||||
}
|
|
||||||
else if (string.Equals(Input, "N", StringComparison.OrdinalIgnoreCase))
|
|
||||||
{
|
|
||||||
IsInputValid = true;
|
|
||||||
No();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
ResetInput();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void ResetInput(string error = "Input Invalid")
|
|
||||||
{
|
|
||||||
Console.Write(string.Format("[{0}] {1}", "Error", error));
|
|
||||||
Wait(150);
|
|
||||||
ClearCurrentConsoleLine();
|
|
||||||
Console.SetCursorPosition(0, Console.CursorTop - 1);
|
|
||||||
ClearCurrentConsoleLine();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void ClearCurrentConsoleLine()
|
|
||||||
{
|
|
||||||
int currentLineCursor = Console.CursorTop;
|
|
||||||
Console.SetCursorPosition(0, Console.CursorTop);
|
|
||||||
Console.Write(new string(' ', Console.BufferWidth));
|
|
||||||
Console.SetCursorPosition(0, currentLineCursor);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
485
ExeToBat/Console.cs
Normal file
485
ExeToBat/Console.cs
Normal file
|
|
@ -0,0 +1,485 @@
|
||||||
|
using static ExeToBat.Generator;
|
||||||
|
using static System.ConsoleUtils;
|
||||||
|
using Mono.Options;
|
||||||
|
using System.Text.Json;
|
||||||
|
using System.Reflection;
|
||||||
|
|
||||||
|
namespace ExeToBat
|
||||||
|
{
|
||||||
|
class Console
|
||||||
|
{
|
||||||
|
public static void Main(string[] args) => new Console().Show(args);
|
||||||
|
|
||||||
|
public Console() { }
|
||||||
|
|
||||||
|
private readonly Generator generator = new();
|
||||||
|
|
||||||
|
public void Show(string[] args)
|
||||||
|
{
|
||||||
|
string? config = null;
|
||||||
|
bool help = false;
|
||||||
|
|
||||||
|
OptionSet options =
|
||||||
|
new()
|
||||||
|
{
|
||||||
|
{
|
||||||
|
"c|config=",
|
||||||
|
"the config file used for automatic generation",
|
||||||
|
v => config = v
|
||||||
|
},
|
||||||
|
{ "h|help", "show this message and exit", v => help = v != null },
|
||||||
|
};
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
List<string> extra = options.Parse(args);
|
||||||
|
if (help)
|
||||||
|
{
|
||||||
|
Help(options);
|
||||||
|
}
|
||||||
|
else if (config != null)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
generator.LoadConfig(GeneratorConfig.FromJson(File.ReadAllText(config)));
|
||||||
|
Generate(interactive: false);
|
||||||
|
}
|
||||||
|
catch (Exception e) when (e is IOException || e is JsonException)
|
||||||
|
{
|
||||||
|
System.Console.WriteLine("Failed to load config {0}:\n{1}", config, e.Message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
MainMenu();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (OptionException e)
|
||||||
|
{
|
||||||
|
System.Console.WriteLine("Invalid arguments: {0}", e.Message);
|
||||||
|
Help(options);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Show() => MainMenu();
|
||||||
|
|
||||||
|
private void Help(OptionSet options)
|
||||||
|
{
|
||||||
|
string version = Assembly.GetExecutingAssembly().GetName().Version?.ToString() ?? "";
|
||||||
|
System.Console.WriteLine();
|
||||||
|
System.Console.WriteLine(string.Format("ExeToBat {0}", version));
|
||||||
|
System.Console.WriteLine();
|
||||||
|
System.Console.WriteLine("From github.com/clragon/ExeToBat");
|
||||||
|
System.Console.WriteLine("Licensed under GNUPL v3");
|
||||||
|
System.Console.WriteLine();
|
||||||
|
System.Console.WriteLine("Usage:");
|
||||||
|
options.WriteOptionDescriptions(System.Console.Out);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void MainMenu()
|
||||||
|
{
|
||||||
|
Dictionary<string, Action> options =
|
||||||
|
new()
|
||||||
|
{
|
||||||
|
{ "Files", ChooseSource },
|
||||||
|
{ "Save config", SaveConfig },
|
||||||
|
{ "Load config", LoadConfig },
|
||||||
|
{ "Generate", Generate },
|
||||||
|
};
|
||||||
|
|
||||||
|
new ListMenu<string>(options.Keys.ToList())
|
||||||
|
{
|
||||||
|
DisplayTitle = (Options) => System.Console.WriteLine("ExeToBat"),
|
||||||
|
DisplayEntry = (Options, index, i) =>
|
||||||
|
{
|
||||||
|
System.Console.WriteLine("[{0}] {1}", i, Options[index]);
|
||||||
|
},
|
||||||
|
HandleEntry = (Options, index) =>
|
||||||
|
{
|
||||||
|
options[Options[index]]();
|
||||||
|
return false;
|
||||||
|
},
|
||||||
|
ExitEntry = "Exit",
|
||||||
|
}.Show();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void SaveConfig()
|
||||||
|
{
|
||||||
|
bool IsInputValid = false;
|
||||||
|
while (!IsInputValid)
|
||||||
|
{
|
||||||
|
System.Console.Clear();
|
||||||
|
System.Console.WriteLine("ExeToBat > Config > Save");
|
||||||
|
System.Console.WriteLine();
|
||||||
|
System.Console.Write("{0}> ", "Output File");
|
||||||
|
string? input = System.Console.ReadLine();
|
||||||
|
|
||||||
|
input = input?.Trim()?.Replace("\"", "");
|
||||||
|
if (!string.IsNullOrEmpty(input))
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
File.WriteAllText(input, generator.SaveConfig().ToJson());
|
||||||
|
IsInputValid = true;
|
||||||
|
}
|
||||||
|
catch (IOException e)
|
||||||
|
{
|
||||||
|
System.Console.WriteLine("Failed to save config:\n{0}", e.Message);
|
||||||
|
ResetInput();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void LoadConfig()
|
||||||
|
{
|
||||||
|
bool IsInputValid = false;
|
||||||
|
while (!IsInputValid)
|
||||||
|
{
|
||||||
|
System.Console.Clear();
|
||||||
|
System.Console.WriteLine("ExeToBat > Config > Load");
|
||||||
|
System.Console.WriteLine();
|
||||||
|
System.Console.Write("{0}> ", "Config File");
|
||||||
|
string? input = System.Console.ReadLine();
|
||||||
|
|
||||||
|
input = input?.Trim()?.Replace("\"", "");
|
||||||
|
if (!string.IsNullOrEmpty(input))
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
generator.LoadConfig(GeneratorConfig.FromJson(File.ReadAllText(input)));
|
||||||
|
IsInputValid = true;
|
||||||
|
}
|
||||||
|
catch (Exception e) when (e is IOException || e is JsonException)
|
||||||
|
{
|
||||||
|
System.Console.WriteLine("Failed to load config {0}:\n{1}", input, e.Message);
|
||||||
|
ResetInput();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ChooseSource()
|
||||||
|
{
|
||||||
|
new ListMenu<SourceFile>(generator.Sources)
|
||||||
|
{
|
||||||
|
DisplayTitle = (sources) =>
|
||||||
|
{
|
||||||
|
System.Console.WriteLine("ExeToBat > Files");
|
||||||
|
System.Console.WriteLine(
|
||||||
|
"[{0}] ({1})",
|
||||||
|
Convert.ToString(0).PadLeft(Convert.ToString(sources.Count).Length, ' '),
|
||||||
|
"Add Files"
|
||||||
|
);
|
||||||
|
},
|
||||||
|
DisplayEntry = (sources, index, i) =>
|
||||||
|
System.Console.WriteLine(
|
||||||
|
"[{0}] {1}",
|
||||||
|
Convert.ToString(i).PadLeft(Convert.ToString(sources.Count).Length, ' '),
|
||||||
|
Path.GetFileName(sources[index].Path)
|
||||||
|
),
|
||||||
|
ZeroEntry = (sources) =>
|
||||||
|
{
|
||||||
|
AddSource();
|
||||||
|
return false;
|
||||||
|
},
|
||||||
|
HandleEntry = (sources, index) =>
|
||||||
|
{
|
||||||
|
ManageSource(sources[index]);
|
||||||
|
return false;
|
||||||
|
},
|
||||||
|
RefreshEntries = (sources) => generator.Sources,
|
||||||
|
}.Show();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void AddSource()
|
||||||
|
{
|
||||||
|
bool IsInputValid = false;
|
||||||
|
while (!IsInputValid)
|
||||||
|
{
|
||||||
|
System.Console.Clear();
|
||||||
|
System.Console.WriteLine("ExeToBat > Files > Add");
|
||||||
|
System.Console.WriteLine();
|
||||||
|
System.Console.Write("{0}> ", "File/Folder");
|
||||||
|
string? input = System.Console.ReadLine();
|
||||||
|
|
||||||
|
input = input?.Trim()?.Replace("\"", "");
|
||||||
|
switch (input)
|
||||||
|
{
|
||||||
|
case var i when string.IsNullOrEmpty(i):
|
||||||
|
IsInputValid = true;
|
||||||
|
break;
|
||||||
|
case var i when Directory.Exists(i):
|
||||||
|
IsInputValid = true;
|
||||||
|
foreach (string file in Directory.GetFiles(i))
|
||||||
|
{
|
||||||
|
generator.Sources.Add(new SourceFile(file));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case var i when File.Exists(i):
|
||||||
|
IsInputValid = true;
|
||||||
|
generator.Sources.Add(new SourceFile(i));
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
ResetInput();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ManageSource(SourceFile source)
|
||||||
|
{
|
||||||
|
Dictionary<string, Func<bool>> options =
|
||||||
|
new()
|
||||||
|
{
|
||||||
|
{
|
||||||
|
"Edit",
|
||||||
|
() =>
|
||||||
|
{
|
||||||
|
ModifySource(source);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Position",
|
||||||
|
() =>
|
||||||
|
{
|
||||||
|
EditPosition(source);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Delete",
|
||||||
|
() =>
|
||||||
|
{
|
||||||
|
generator.Sources.Remove(source);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
new ListMenu<string>(options.Keys.ToList())
|
||||||
|
{
|
||||||
|
DisplayTitle = (Options) =>
|
||||||
|
System.Console.WriteLine(
|
||||||
|
"ExeToBat > Files > {0}",
|
||||||
|
Path.GetFileName(source.Path)
|
||||||
|
),
|
||||||
|
DisplayEntry = (Options, index, i) =>
|
||||||
|
System.Console.WriteLine("[{0}] {1}", i, Options[index]),
|
||||||
|
HandleEntry = (Options, index) => options[Options[index]](),
|
||||||
|
}.Show();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ModifySource(SourceFile source)
|
||||||
|
{
|
||||||
|
List<(string, string, Action)> options()
|
||||||
|
{
|
||||||
|
List<(string, string, Action)> result =
|
||||||
|
new()
|
||||||
|
{
|
||||||
|
("File", source.Path, () => { }),
|
||||||
|
("Extraction directory", source.Directory, () => EditExtraction(source)),
|
||||||
|
(
|
||||||
|
"Execute after extraction",
|
||||||
|
source.Execute.ToString(),
|
||||||
|
() => source.Execute = !source.Execute
|
||||||
|
),
|
||||||
|
};
|
||||||
|
if (source.Execute)
|
||||||
|
{
|
||||||
|
result.Add(("Parameters", source.Parameters, () => EditParameters(source)));
|
||||||
|
result.Add(
|
||||||
|
("Wait for exit", source.Wait.ToString(), () => source.Wait = !source.Wait)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (source.Execute && source.Wait)
|
||||||
|
{
|
||||||
|
result.Add(
|
||||||
|
(
|
||||||
|
"Delete after execution",
|
||||||
|
source.Delete.ToString(),
|
||||||
|
() => source.Delete = !source.Delete
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
new ListMenu<(string, string, Action)>(options())
|
||||||
|
{
|
||||||
|
DisplayTitle = (Options) =>
|
||||||
|
System.Console.WriteLine(
|
||||||
|
"ExeToBat > Files > {0} > Edit",
|
||||||
|
Path.GetFileName(source.Path)
|
||||||
|
),
|
||||||
|
DisplayEntry = (Options, index, i) =>
|
||||||
|
{
|
||||||
|
int MaxLength = options().ConvertAll(e => e.Item1.Length).Max();
|
||||||
|
System.Console.WriteLine(
|
||||||
|
"[{0}] {1} | {2}",
|
||||||
|
i,
|
||||||
|
Options[index].Item1.PadRight(MaxLength, ' '),
|
||||||
|
Options[index].Item2
|
||||||
|
);
|
||||||
|
},
|
||||||
|
HandleEntry = (Options, index) =>
|
||||||
|
{
|
||||||
|
Options[index].Item3();
|
||||||
|
return false;
|
||||||
|
},
|
||||||
|
RefreshEntries = (Options) => options(),
|
||||||
|
}.Show();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void EditExtraction(SourceFile source)
|
||||||
|
{
|
||||||
|
System.Console.Clear();
|
||||||
|
System.Console.WriteLine(
|
||||||
|
"ExeToBat > Files > {0} > Edit > Extraction",
|
||||||
|
Path.GetFileName(source.Path)
|
||||||
|
);
|
||||||
|
System.Console.WriteLine();
|
||||||
|
System.Console.WriteLine("Documentation: ");
|
||||||
|
System.Console.WriteLine("https://ss64.com/nt/syntax-variables.html");
|
||||||
|
System.Console.WriteLine("https://ss64.com/nt/syntax-args.html");
|
||||||
|
System.Console.WriteLine();
|
||||||
|
System.Console.Write("{0}> ", "Directory");
|
||||||
|
string? input = System.Console.ReadLine();
|
||||||
|
|
||||||
|
if (!string.IsNullOrEmpty(input))
|
||||||
|
{
|
||||||
|
source.Directory = input;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void EditParameters(SourceFile source)
|
||||||
|
{
|
||||||
|
System.Console.Clear();
|
||||||
|
System.Console.WriteLine(
|
||||||
|
"ExeToBat > Files > {0} > Edit > Parameters",
|
||||||
|
Path.GetFileName(source.Path)
|
||||||
|
);
|
||||||
|
System.Console.WriteLine();
|
||||||
|
System.Console.Write("{0}> ", "Parameters");
|
||||||
|
string? input = System.Console.ReadLine();
|
||||||
|
|
||||||
|
input = input?.Trim();
|
||||||
|
if (!string.IsNullOrEmpty(input))
|
||||||
|
{
|
||||||
|
source.Parameters = input;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void EditPosition(SourceFile source)
|
||||||
|
{
|
||||||
|
bool IsInputValid = false;
|
||||||
|
while (!IsInputValid)
|
||||||
|
{
|
||||||
|
System.Console.Clear();
|
||||||
|
System.Console.WriteLine(
|
||||||
|
"ExeToBat > Files > {0} > Position : {1}",
|
||||||
|
Path.GetFileName(source.Path),
|
||||||
|
generator.Sources.IndexOf(source)
|
||||||
|
);
|
||||||
|
|
||||||
|
System.Console.WriteLine();
|
||||||
|
System.Console.Write("{0}> ", "New index");
|
||||||
|
string? input = System.Console.ReadLine();
|
||||||
|
|
||||||
|
if (int.TryParse(input, out int index))
|
||||||
|
{
|
||||||
|
if (index < generator.Sources.Count)
|
||||||
|
{
|
||||||
|
generator.Sources.Remove(source);
|
||||||
|
generator.Sources.Insert(index, source);
|
||||||
|
IsInputValid = true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ResetInput();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(input))
|
||||||
|
{
|
||||||
|
IsInputValid = true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ResetInput();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Generate() => Generate(true);
|
||||||
|
|
||||||
|
private void Generate(bool interactive)
|
||||||
|
{
|
||||||
|
System.Console.Clear();
|
||||||
|
System.Console.WriteLine("ExeToBat > Generate");
|
||||||
|
|
||||||
|
generator.Generation += OnGenerate;
|
||||||
|
|
||||||
|
generator.Generate("output.bat");
|
||||||
|
|
||||||
|
generator.Generation -= OnGenerate;
|
||||||
|
|
||||||
|
if (interactive)
|
||||||
|
{
|
||||||
|
System.Console.WriteLine("Press anything...");
|
||||||
|
System.Console.ReadKey();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnGenerate(object? sender, GeneratorEvent e)
|
||||||
|
{
|
||||||
|
switch (e)
|
||||||
|
{
|
||||||
|
case GenerationStartEvent s:
|
||||||
|
System.Console.WriteLine("Starting generation...");
|
||||||
|
System.Console.WriteLine("{0} files scheduled", s.Files.Count);
|
||||||
|
break;
|
||||||
|
case ReadingFileEvent s:
|
||||||
|
System.Console.WriteLine("[{0}] Reading file", s.File.Path);
|
||||||
|
break;
|
||||||
|
case WritingFilePartEvent s:
|
||||||
|
System.Console.Write(
|
||||||
|
"[{0}] writing part {1}/{2}\r",
|
||||||
|
s.File.Path,
|
||||||
|
s.Part.ToString().PadLeft(s.Max.ToString().Length, '0'),
|
||||||
|
s.Max
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
case WritingFileDecoderEvent s:
|
||||||
|
System.Console.WriteLine();
|
||||||
|
System.Console.WriteLine("[{0}] Writing decode mechanism", s.File.Path);
|
||||||
|
break;
|
||||||
|
case WritingFileExecuteEvent s:
|
||||||
|
System.Console.WriteLine("[{0}] Writing execute mechanism", s.File.Path);
|
||||||
|
break;
|
||||||
|
case WritingFileWaitEvent s:
|
||||||
|
System.Console.WriteLine("[{0}] Writing wait mechanism", s.File.Path);
|
||||||
|
break;
|
||||||
|
case WritingFileDeleteEvent s:
|
||||||
|
System.Console.WriteLine("[{0}] Writing delete mechanism", s.File.Path);
|
||||||
|
break;
|
||||||
|
case WritingFileCompleteEvent s:
|
||||||
|
System.Console.WriteLine("[{0}] Finshed generating!", s.File.Path);
|
||||||
|
break;
|
||||||
|
case GenerationCompleteEvent s:
|
||||||
|
System.Console.WriteLine("Finished generation! Result written to {0}", s.Path);
|
||||||
|
break;
|
||||||
|
case GenerationEmptyEvent _:
|
||||||
|
System.Console.WriteLine("No files specified");
|
||||||
|
break;
|
||||||
|
case GenerationFailedEvent s:
|
||||||
|
System.Console.WriteLine("Generation failed with:\n{0}", s.Error.Message);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
328
ExeToBat/ConsoleUtils.cs
Normal file
328
ExeToBat/ConsoleUtils.cs
Normal file
|
|
@ -0,0 +1,328 @@
|
||||||
|
namespace System
|
||||||
|
{
|
||||||
|
public static class ConsoleUtils
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Pauses the thread for a duration.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="ms"></param> The duration to pause.
|
||||||
|
public static void Wait(int ms)
|
||||||
|
{
|
||||||
|
using (Threading.ManualResetEvent wait = new Threading.ManualResetEvent(false))
|
||||||
|
{
|
||||||
|
wait.WaitOne(ms);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Clears the current line of the Console.
|
||||||
|
/// </summary>
|
||||||
|
public static void ClearCurrentConsoleLine()
|
||||||
|
{
|
||||||
|
int currentLineCursor = Console.CursorTop;
|
||||||
|
Console.SetCursorPosition(0, Console.CursorTop);
|
||||||
|
Console.Write(new string(' ', Console.BufferWidth));
|
||||||
|
Console.SetCursorPosition(0, currentLineCursor);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Deletes the last line of the console and displays an error for a short duration.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="error"></param> The error to display.
|
||||||
|
public static void ResetInput(string error = "Input Invalid")
|
||||||
|
{
|
||||||
|
Console.Write(string.Format("[{0}] {1}", "Error", error));
|
||||||
|
Wait(150);
|
||||||
|
ClearCurrentConsoleLine();
|
||||||
|
Console.SetCursorPosition(0, Console.CursorTop - 1);
|
||||||
|
ClearCurrentConsoleLine();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A simple template to create Yes or No menus.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="title">The title of the menu.</param>
|
||||||
|
/// <param name="Yes">The function to be called upon Yes</param>
|
||||||
|
/// <param name="No">The function to be called upon No</param>
|
||||||
|
public static void YesNoMenu(string title, Action Yes, Action No)
|
||||||
|
{
|
||||||
|
bool IsInputValid = false;
|
||||||
|
while (!IsInputValid)
|
||||||
|
{
|
||||||
|
Console.Write("{0}? [{1}]> ", title, "Y/N");
|
||||||
|
string Input = Console.ReadKey().KeyChar.ToString();
|
||||||
|
Wait(20);
|
||||||
|
Console.Write("\n");
|
||||||
|
if (string.Equals(Input, "Y", StringComparison.OrdinalIgnoreCase))
|
||||||
|
{
|
||||||
|
IsInputValid = true;
|
||||||
|
Yes();
|
||||||
|
}
|
||||||
|
else if (string.Equals(Input, "N", StringComparison.OrdinalIgnoreCase))
|
||||||
|
{
|
||||||
|
IsInputValid = true;
|
||||||
|
No();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ResetInput();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class ListMenu<T>
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Command-line List Options Enumerator
|
||||||
|
/// <para>Turns a List<T> into a menu of options. Each list item is asigned a number. Provides several callbacks to customise the menu.</para>
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="entries">List of objects you want to display</param>
|
||||||
|
public ListMenu(List<T> entries)
|
||||||
|
{
|
||||||
|
Entries = entries;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The prompt that is displayed to the user.
|
||||||
|
/// </summary>
|
||||||
|
public string Prompt = "Choose";
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The string to be displayed for the option to exit the menu.
|
||||||
|
/// </summary>
|
||||||
|
public string ExitEntry = "Back";
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The key the user has to press to exit the menu.
|
||||||
|
/// </summary>
|
||||||
|
public char ExitKey = 'q';
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Wether or not the user can exit the menu.
|
||||||
|
/// </summary>
|
||||||
|
public bool UserCanExit = true;
|
||||||
|
|
||||||
|
private List<T> Entries;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The function that processes the chosen menu entries.
|
||||||
|
/// </summary>
|
||||||
|
public Func<List<T>, int, bool> HandleEntry = (entries, index) =>
|
||||||
|
{
|
||||||
|
Console.Clear();
|
||||||
|
Console.WriteLine(entries[index]);
|
||||||
|
Wait(200);
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The function that displays the menu title.
|
||||||
|
/// </summary>
|
||||||
|
public Action<List<T>> DisplayTitle = (entries) => { };
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The function that displays the entry to the user.
|
||||||
|
/// </summary>
|
||||||
|
public Action<List<T>, int, int> DisplayEntry = (entries, index, num) =>
|
||||||
|
{
|
||||||
|
Console.WriteLine(
|
||||||
|
"[{0}] {1}",
|
||||||
|
Convert.ToString(num).PadLeft(Convert.ToString(entries.Count).Length, ' '),
|
||||||
|
entries[index]
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The function to update the list of entries.
|
||||||
|
/// </summary>
|
||||||
|
public Func<List<T>, List<T>> RefreshEntries = (entries) => entries;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The function that is called when 0th entry in the list is chosen.
|
||||||
|
/// <para>Display this entry with the title function.</para>
|
||||||
|
/// </summary>
|
||||||
|
public Func<List<T>, bool> ZeroEntry = (entries) =>
|
||||||
|
{
|
||||||
|
ResetInput();
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Display the menu.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns></returns>
|
||||||
|
public ListMenu<T> Show()
|
||||||
|
{
|
||||||
|
string readInput = string.Empty;
|
||||||
|
bool MenuExitIsPending = false;
|
||||||
|
while (!MenuExitIsPending)
|
||||||
|
{
|
||||||
|
Console.Clear();
|
||||||
|
int printedEntries = 0;
|
||||||
|
Entries = RefreshEntries(Entries);
|
||||||
|
DisplayTitle(Entries);
|
||||||
|
if (Entries.Any())
|
||||||
|
{
|
||||||
|
int num = 0;
|
||||||
|
foreach (T entry in Entries)
|
||||||
|
{
|
||||||
|
num++;
|
||||||
|
if (
|
||||||
|
string.IsNullOrEmpty(readInput)
|
||||||
|
|| Convert.ToString(num).StartsWith(readInput)
|
||||||
|
)
|
||||||
|
{
|
||||||
|
DisplayEntry(Entries, Entries.IndexOf(entry), num);
|
||||||
|
printedEntries++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Entries.Count > Console.WindowHeight - 5)
|
||||||
|
{
|
||||||
|
if (printedEntries >= Console.WindowHeight - (5 + 1))
|
||||||
|
{
|
||||||
|
Console.WriteLine(
|
||||||
|
"[{0}] +{1}",
|
||||||
|
".".PadLeft(Convert.ToString(Entries.Count).Length, '.'),
|
||||||
|
Entries.Count
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (printedEntries == Console.WindowHeight - 5)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (UserCanExit)
|
||||||
|
{
|
||||||
|
Console.WriteLine(
|
||||||
|
"[{0}] {1}",
|
||||||
|
Convert
|
||||||
|
.ToString(ExitKey)
|
||||||
|
.PadLeft(Convert.ToString(Entries.Count).Length, ' '),
|
||||||
|
ExitEntry
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Console.WriteLine();
|
||||||
|
|
||||||
|
bool InputIsValid = false;
|
||||||
|
while (!InputIsValid)
|
||||||
|
{
|
||||||
|
Console.Write("{0}> {1}", Prompt, readInput);
|
||||||
|
ConsoleKeyInfo input = Console.ReadKey();
|
||||||
|
Wait(20);
|
||||||
|
int choiceNum = -1;
|
||||||
|
switch (input)
|
||||||
|
{
|
||||||
|
case var key when key.KeyChar.Equals(ExitKey):
|
||||||
|
if (UserCanExit)
|
||||||
|
{
|
||||||
|
Console.WriteLine();
|
||||||
|
InputIsValid = true;
|
||||||
|
MenuExitIsPending = true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Console.WriteLine();
|
||||||
|
ResetInput();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case var key when key.Key.Equals(ConsoleKey.Backspace):
|
||||||
|
if (!string.IsNullOrEmpty(readInput))
|
||||||
|
{
|
||||||
|
Console.Write("\b");
|
||||||
|
readInput = readInput.Remove(readInput.Length - 1);
|
||||||
|
}
|
||||||
|
InputIsValid = true;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case var key when key.Key.Equals(ConsoleKey.Enter):
|
||||||
|
if (!string.IsNullOrEmpty(readInput))
|
||||||
|
{
|
||||||
|
if (HandleEntry(Entries, (Convert.ToInt32(readInput) - 1)))
|
||||||
|
{
|
||||||
|
MenuExitIsPending = true;
|
||||||
|
}
|
||||||
|
readInput = string.Empty;
|
||||||
|
}
|
||||||
|
InputIsValid = true;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case var key when int.TryParse(key.KeyChar.ToString(), out choiceNum):
|
||||||
|
Console.WriteLine();
|
||||||
|
if (string.IsNullOrEmpty(readInput) && choiceNum.Equals(0))
|
||||||
|
{
|
||||||
|
InputIsValid = true;
|
||||||
|
if (ZeroEntry(Entries))
|
||||||
|
{
|
||||||
|
MenuExitIsPending = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (
|
||||||
|
Convert.ToInt32(readInput + Convert.ToString(choiceNum))
|
||||||
|
<= Entries.Count
|
||||||
|
)
|
||||||
|
{
|
||||||
|
InputIsValid = true;
|
||||||
|
int matchingEntries = 0;
|
||||||
|
readInput = new System.Text.StringBuilder()
|
||||||
|
.Append(readInput)
|
||||||
|
.Append(Convert.ToString(choiceNum))
|
||||||
|
.ToString();
|
||||||
|
for (int i = 0; i < Entries.Count; i++)
|
||||||
|
{
|
||||||
|
if (
|
||||||
|
Convert.ToString(i + 1).StartsWith(readInput)
|
||||||
|
|| Convert.ToString(i + 1) == readInput
|
||||||
|
)
|
||||||
|
{
|
||||||
|
matchingEntries++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (
|
||||||
|
(
|
||||||
|
readInput.Length
|
||||||
|
== Convert.ToString(Entries.Count).Length
|
||||||
|
) || (matchingEntries == 1)
|
||||||
|
)
|
||||||
|
{
|
||||||
|
if (
|
||||||
|
HandleEntry(
|
||||||
|
Entries,
|
||||||
|
(Convert.ToInt32(readInput) - 1)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
{
|
||||||
|
MenuExitIsPending = true;
|
||||||
|
}
|
||||||
|
readInput = string.Empty;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ResetInput();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
Console.WriteLine();
|
||||||
|
ResetInput();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,53 +1,29 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
|
||||||
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
|
||||||
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
|
||||||
<ProjectGuid>{04F45237-23C8-4EE6-B61C-6C47B9979A4B}</ProjectGuid>
|
|
||||||
<OutputType>Exe</OutputType>
|
<OutputType>Exe</OutputType>
|
||||||
<RootNamespace>ExeToBat</RootNamespace>
|
<PublishSingleFile>true</PublishSingleFile>
|
||||||
<AssemblyName>ExeToBat</AssemblyName>
|
<PublishReadyToRun>true</PublishReadyToRun>
|
||||||
<TargetFrameworkVersion>v4.6.1</TargetFrameworkVersion>
|
<TargetFramework>net6.0</TargetFramework>
|
||||||
<FileAlignment>512</FileAlignment>
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
|
<Nullable>enable</Nullable>
|
||||||
<Deterministic>true</Deterministic>
|
<AssemblyVersion>2.0.0.0</AssemblyVersion>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
|
||||||
<PlatformTarget>AnyCPU</PlatformTarget>
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
|
||||||
<DebugSymbols>true</DebugSymbols>
|
<WarningsAsErrors>$(WarningsAsErrors);NU1605;CS8600;CS8601;CS8602;CS8603;CS8604;CS8613;CS8614;CS8619;CS8620;CS8622;CS8625;CS8629;CS8633;CS8767;</WarningsAsErrors>
|
||||||
<DebugType>full</DebugType>
|
|
||||||
<Optimize>false</Optimize>
|
|
||||||
<OutputPath>bin\Debug\</OutputPath>
|
|
||||||
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
|
||||||
<ErrorReport>prompt</ErrorReport>
|
|
||||||
<WarningLevel>4</WarningLevel>
|
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
|
||||||
<PlatformTarget>AnyCPU</PlatformTarget>
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
|
||||||
<DebugType>pdbonly</DebugType>
|
<WarningsAsErrors>$(WarningsAsErrors);NU1605;CS8600;CS8601;CS8602;CS8603;CS8604;CS8613;CS8614;CS8619;CS8620;CS8622;CS8625;CS8629;CS8633;CS8767;</WarningsAsErrors>
|
||||||
<Optimize>true</Optimize>
|
|
||||||
<OutputPath>bin\Release\</OutputPath>
|
|
||||||
<DefineConstants>TRACE</DefineConstants>
|
|
||||||
<ErrorReport>prompt</ErrorReport>
|
|
||||||
<WarningLevel>4</WarningLevel>
|
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Reference Include="System" />
|
<None Include="..\.editorconfig" Link=".editorconfig" />
|
||||||
<Reference Include="System.Core" />
|
|
||||||
<Reference Include="System.Xml.Linq" />
|
|
||||||
<Reference Include="System.Data.DataSetExtensions" />
|
|
||||||
<Reference Include="Microsoft.CSharp" />
|
|
||||||
<Reference Include="System.Data" />
|
|
||||||
<Reference Include="System.Net.Http" />
|
|
||||||
<Reference Include="System.Xml" />
|
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Compile Include="BatGen.cs" />
|
<PackageReference Include="Mono.Options" Version="6.12.0.148" />
|
||||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
|
||||||
<None Include="App.config" />
|
|
||||||
</ItemGroup>
|
|
||||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
|
||||||
</Project>
|
</Project>
|
||||||
311
ExeToBat/Generator.cs
Normal file
311
ExeToBat/Generator.cs
Normal file
|
|
@ -0,0 +1,311 @@
|
||||||
|
using System.Text.Json;
|
||||||
|
|
||||||
|
namespace ExeToBat
|
||||||
|
{
|
||||||
|
public class Generator
|
||||||
|
{
|
||||||
|
public Generator() { }
|
||||||
|
|
||||||
|
public Generator(GeneratorConfig config)
|
||||||
|
{
|
||||||
|
LoadConfig(config);
|
||||||
|
}
|
||||||
|
|
||||||
|
public const int ChunkSize = 8000;
|
||||||
|
|
||||||
|
public List<SourceFile> Sources = new();
|
||||||
|
|
||||||
|
public class SourceFile
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Represents a file that is later embedded in a bat.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="path"></param> The path of the file.
|
||||||
|
public SourceFile(string path)
|
||||||
|
{
|
||||||
|
Path = path;
|
||||||
|
}
|
||||||
|
|
||||||
|
public string Path { get; set; }
|
||||||
|
public bool Execute { get; set; } = false;
|
||||||
|
public bool Wait { get; set; } = false;
|
||||||
|
public bool Delete { get; set; } = false;
|
||||||
|
public string Resource { get; set; } = GetTempFileName();
|
||||||
|
public string Directory { get; set; } = "%~dp0";
|
||||||
|
public string Parameters { get; set; } = "";
|
||||||
|
|
||||||
|
static public string GetTempFileName()
|
||||||
|
{
|
||||||
|
return string.Format($"res_{new Random().Next(1000, 10000)}.b64");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class GeneratorConfig
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The configuration for a Generator.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="sources"></param>
|
||||||
|
public GeneratorConfig(List<SourceFile> sources)
|
||||||
|
{
|
||||||
|
Sources = sources;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<SourceFile> Sources { get; private set; }
|
||||||
|
|
||||||
|
public string ToJson()
|
||||||
|
{
|
||||||
|
return JsonSerializer.Serialize(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static GeneratorConfig FromJson(string raw)
|
||||||
|
{
|
||||||
|
return JsonSerializer.Deserialize<GeneratorConfig>(raw)
|
||||||
|
?? new GeneratorConfig(new());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Exports the variables of this Generator as a configuration.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns></returns>
|
||||||
|
public GeneratorConfig SaveConfig()
|
||||||
|
{
|
||||||
|
return new GeneratorConfig(Sources);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Loads a configuration into this Generator.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="config"></param>
|
||||||
|
public void LoadConfig(GeneratorConfig config)
|
||||||
|
{
|
||||||
|
Sources = config.Sources;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Sends progress updates about ongoing generation task.
|
||||||
|
/// </summary>
|
||||||
|
public event EventHandler<GeneratorEvent>? Generation;
|
||||||
|
|
||||||
|
protected virtual void OnGeneration(GeneratorEvent e)
|
||||||
|
{
|
||||||
|
EventHandler<GeneratorEvent>? handler = Generation;
|
||||||
|
handler?.Invoke(this, e);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Generates a batch file with all specified source files.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="outputFile"></param> Target output file path.
|
||||||
|
public void Generate(string outputFile)
|
||||||
|
{
|
||||||
|
if (Sources.Any())
|
||||||
|
{
|
||||||
|
using StreamWriter writer = new(outputFile);
|
||||||
|
OnGeneration(new GenerationStartEvent(Sources));
|
||||||
|
writer.WriteLine("@echo off");
|
||||||
|
writer.WriteLine(":: Auto-generated batch file by ExeToBat ::");
|
||||||
|
writer.WriteLine("");
|
||||||
|
|
||||||
|
foreach (SourceFile source in Sources)
|
||||||
|
{
|
||||||
|
OnGeneration(new ReadingFileEvent(source));
|
||||||
|
List<string> fileChunks = Convert
|
||||||
|
.ToBase64String(File.ReadAllBytes(source.Path))
|
||||||
|
.Chunks(ChunkSize)
|
||||||
|
.ToList();
|
||||||
|
string tempFile = Path.Combine("%temp%", source.Resource);
|
||||||
|
writer.WriteLine("(");
|
||||||
|
|
||||||
|
int pos = 0;
|
||||||
|
foreach (string part in fileChunks)
|
||||||
|
{
|
||||||
|
pos++;
|
||||||
|
OnGeneration(new WritingFilePartEvent(source, pos, fileChunks.Count));
|
||||||
|
writer.WriteLine(string.Format("echo {0}", part));
|
||||||
|
}
|
||||||
|
|
||||||
|
writer.WriteLine(string.Format(") >> \"{0}\"", tempFile));
|
||||||
|
writer.WriteLine("");
|
||||||
|
|
||||||
|
OnGeneration(new WritingFileDecoderEvent(source));
|
||||||
|
writer.WriteLine(
|
||||||
|
string.Format(
|
||||||
|
"certutil -decode \"{0}\" \"{1}\" >nul 2>&1",
|
||||||
|
tempFile,
|
||||||
|
Path.Combine(source.Directory, Path.GetFileName(source.Path))
|
||||||
|
)
|
||||||
|
);
|
||||||
|
writer.WriteLine(string.Format("del /f /q \"{0}\" >nul 2>&1", tempFile));
|
||||||
|
writer.WriteLine("");
|
||||||
|
|
||||||
|
if (source.Execute)
|
||||||
|
{
|
||||||
|
string wait;
|
||||||
|
if (source.Wait)
|
||||||
|
{
|
||||||
|
wait = " /wait";
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
wait = " ";
|
||||||
|
}
|
||||||
|
|
||||||
|
OnGeneration(new WritingFileExecuteEvent(source));
|
||||||
|
writer.WriteLine(
|
||||||
|
string.Format(
|
||||||
|
"start{0} \"\" \"cmd /c {1}\" {2}",
|
||||||
|
wait,
|
||||||
|
Path.Combine(source.Directory, Path.GetFileName(source.Path)),
|
||||||
|
source.Parameters
|
||||||
|
)
|
||||||
|
);
|
||||||
|
if (source.Wait)
|
||||||
|
{
|
||||||
|
OnGeneration(new WritingFileWaitEvent(source));
|
||||||
|
if (source.Delete)
|
||||||
|
{
|
||||||
|
OnGeneration(new WritingFileDeleteEvent(source));
|
||||||
|
writer.WriteLine(
|
||||||
|
string.Format(
|
||||||
|
"del /f /q \"{0}\" >nul 2>&1",
|
||||||
|
Path.Combine(
|
||||||
|
source.Directory,
|
||||||
|
Path.GetFileName(source.Path)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
writer.WriteLine("");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
writer.WriteLine("");
|
||||||
|
}
|
||||||
|
|
||||||
|
writer.Flush();
|
||||||
|
OnGeneration(new WritingFileCompleteEvent(source));
|
||||||
|
}
|
||||||
|
|
||||||
|
OnGeneration(new GenerationCompleteEvent(outputFile));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
OnGeneration(new GenerationEmptyEvent());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public abstract class GeneratorEvent : EventArgs { }
|
||||||
|
|
||||||
|
public abstract class GeneratorFileEvent : GeneratorEvent
|
||||||
|
{
|
||||||
|
public GeneratorFileEvent(SourceFile file)
|
||||||
|
{
|
||||||
|
File = file;
|
||||||
|
}
|
||||||
|
|
||||||
|
public SourceFile File { get; private set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class GenerationStartEvent : GeneratorEvent
|
||||||
|
{
|
||||||
|
public GenerationStartEvent(List<SourceFile> files)
|
||||||
|
{
|
||||||
|
Files = files;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<SourceFile> Files { get; private set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class ReadingFileEvent : GeneratorFileEvent
|
||||||
|
{
|
||||||
|
public ReadingFileEvent(SourceFile file) : base(file) { }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class WritingFilePartEvent : GeneratorFileEvent
|
||||||
|
{
|
||||||
|
public WritingFilePartEvent(SourceFile file, int part, int max) : base(file)
|
||||||
|
{
|
||||||
|
Part = part;
|
||||||
|
Max = max;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int Part { get; private set; }
|
||||||
|
public int Max { get; private set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class WritingFileDecoderEvent : GeneratorFileEvent
|
||||||
|
{
|
||||||
|
public WritingFileDecoderEvent(SourceFile file) : base(file) { }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class WritingFileExecuteEvent : GeneratorFileEvent
|
||||||
|
{
|
||||||
|
public WritingFileExecuteEvent(SourceFile file) : base(file) { }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class WritingFileWaitEvent : GeneratorFileEvent
|
||||||
|
{
|
||||||
|
public WritingFileWaitEvent(SourceFile file) : base(file) { }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class WritingFileDeleteEvent : GeneratorFileEvent
|
||||||
|
{
|
||||||
|
public WritingFileDeleteEvent(SourceFile file) : base(file) { }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class WritingFileCompleteEvent : GeneratorFileEvent
|
||||||
|
{
|
||||||
|
public WritingFileCompleteEvent(SourceFile file) : base(file) { }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class GenerationCompleteEvent : GeneratorEvent
|
||||||
|
{
|
||||||
|
public GenerationCompleteEvent(string path)
|
||||||
|
{
|
||||||
|
Path = path;
|
||||||
|
}
|
||||||
|
|
||||||
|
public string Path { get; private set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class GenerationEmptyEvent : GeneratorEvent
|
||||||
|
{
|
||||||
|
public GenerationEmptyEvent() { }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class GenerationFailedEvent : GeneratorEvent
|
||||||
|
{
|
||||||
|
public GenerationFailedEvent(Exception error)
|
||||||
|
{
|
||||||
|
Error = error;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Exception Error { get; private set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static class StringChunks
|
||||||
|
{
|
||||||
|
public static string[] Chunks(this string toSplit, int chunkSize)
|
||||||
|
{
|
||||||
|
int stringLength = toSplit.Length;
|
||||||
|
|
||||||
|
int chunksRequired = (int)Math.Ceiling(stringLength / (decimal)chunkSize);
|
||||||
|
var stringArray = new string[chunksRequired];
|
||||||
|
|
||||||
|
int lengthRemaining = stringLength;
|
||||||
|
|
||||||
|
for (int i = 0; i < chunksRequired; i++)
|
||||||
|
{
|
||||||
|
int lengthToUse = Math.Min(lengthRemaining, chunkSize);
|
||||||
|
int startIndex = chunkSize * i;
|
||||||
|
stringArray[i] = toSplit.Substring(startIndex, lengthToUse);
|
||||||
|
|
||||||
|
lengthRemaining -= lengthToUse;
|
||||||
|
}
|
||||||
|
|
||||||
|
return stringArray;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,36 +0,0 @@
|
||||||
using System.Reflection;
|
|
||||||
using System.Runtime.CompilerServices;
|
|
||||||
using System.Runtime.InteropServices;
|
|
||||||
|
|
||||||
// Allgemeine Informationen über eine Assembly werden über die folgenden
|
|
||||||
// Attribute gesteuert. Ändern Sie diese Attributwerte, um die Informationen zu ändern,
|
|
||||||
// die einer Assembly zugeordnet sind.
|
|
||||||
[assembly: AssemblyTitle("ExeToBat")]
|
|
||||||
[assembly: AssemblyDescription("")]
|
|
||||||
[assembly: AssemblyConfiguration("")]
|
|
||||||
[assembly: AssemblyCompany("")]
|
|
||||||
[assembly: AssemblyProduct("ExeToBat")]
|
|
||||||
[assembly: AssemblyCopyright("Copyright © 2018")]
|
|
||||||
[assembly: AssemblyTrademark("")]
|
|
||||||
[assembly: AssemblyCulture("")]
|
|
||||||
|
|
||||||
// Durch Festlegen von ComVisible auf FALSE werden die Typen in dieser Assembly
|
|
||||||
// für COM-Komponenten unsichtbar. Wenn Sie auf einen Typ in dieser Assembly von
|
|
||||||
// COM aus zugreifen müssen, sollten Sie das ComVisible-Attribut für diesen Typ auf "True" festlegen.
|
|
||||||
[assembly: ComVisible(false)]
|
|
||||||
|
|
||||||
// Die folgende GUID bestimmt die ID der Typbibliothek, wenn dieses Projekt für COM verfügbar gemacht wird
|
|
||||||
[assembly: Guid("04f45237-23c8-4ee6-b61c-6c47b9979a4b")]
|
|
||||||
|
|
||||||
// Versionsinformationen für eine Assembly bestehen aus den folgenden vier Werten:
|
|
||||||
//
|
|
||||||
// Hauptversion
|
|
||||||
// Nebenversion
|
|
||||||
// Buildnummer
|
|
||||||
// Revision
|
|
||||||
//
|
|
||||||
// Sie können alle Werte angeben oder Standardwerte für die Build- und Revisionsnummern verwenden,
|
|
||||||
// übernehmen, indem Sie "*" eingeben:
|
|
||||||
// [assembly: AssemblyVersion("1.0.*")]
|
|
||||||
[assembly: AssemblyVersion("1.0.0.0")]
|
|
||||||
[assembly: AssemblyFileVersion("1.0.0.0")]
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue