Compare commits
15 Commits
main
...
vdf-format
| Author | SHA1 | Date | |
|---|---|---|---|
| 5d2393dd66 | |||
| 4016b89855 | |||
| fe4eb7c74c | |||
| d2aa72e4f5 | |||
| 45402d6482 | |||
| 40e7d29f22 | |||
| 48f93e6f0d | |||
| 4a948b9098 | |||
| 7cc36c1401 | |||
| e5e34e248e | |||
| d6da6ca598 | |||
| aa0d04b094 | |||
| e9b915dec5 | |||
| 2f5214d3b2 | |||
| 61407c3aab |
@ -2,10 +2,17 @@
|
|||||||
|
|
||||||
internal static partial class Kernel32
|
internal static partial class Kernel32
|
||||||
{
|
{
|
||||||
|
[LibraryImport("kernel32.dll", SetLastError = true)]
|
||||||
|
[return: MarshalAs(UnmanagedType.Bool)]
|
||||||
|
public static partial bool GetConsoleScreenBufferInfo(nint hConsoleOutput, out ConsoleScreenBufferInfo lpConsoleScreenBufferInfo);
|
||||||
|
|
||||||
|
[LibraryImport("kernel32.dll", SetLastError = true)]
|
||||||
|
public static partial nint GetConsoleWindow();
|
||||||
|
|
||||||
[LibraryImport("kernel32.dll", SetLastError = true)]
|
[LibraryImport("kernel32.dll", SetLastError = true)]
|
||||||
public static partial uint GetFinalPathNameByHandleA(nint hFile, [MarshalAs(UnmanagedType.LPTStr)] string lpszFilePath,
|
public static partial uint GetFinalPathNameByHandleA(nint hFile, [MarshalAs(UnmanagedType.LPTStr)] string lpszFilePath,
|
||||||
uint cchFilePath, uint dwFlags);
|
uint cchFilePath, uint dwFlags);
|
||||||
|
|
||||||
[LibraryImport("kernel32.dll")]
|
[LibraryImport("kernel32.dll", SetLastError = true)]
|
||||||
public static partial nuint GlobalSize(nint hPtr);
|
public static partial nuint GlobalSize(nint hPtr);
|
||||||
}
|
}
|
||||||
|
|||||||
15
SrcMod/Shell/Interop/ObjectModels/ConsoleScreenBufferInfo.cs
Normal file
15
SrcMod/Shell/Interop/ObjectModels/ConsoleScreenBufferInfo.cs
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
namespace SrcMod.Shell.Interop.ObjectModels;
|
||||||
|
|
||||||
|
[StructLayout(LayoutKind.Sequential)]
|
||||||
|
internal struct ConsoleScreenBufferInfo
|
||||||
|
{
|
||||||
|
[MarshalAs(UnmanagedType.LPStruct)]
|
||||||
|
public Coord dwSize;
|
||||||
|
[MarshalAs(UnmanagedType.LPStruct)]
|
||||||
|
public Coord dwCursorPosition;
|
||||||
|
public int wAttributes;
|
||||||
|
[MarshalAs(UnmanagedType.LPStruct)]
|
||||||
|
public SmallRect srWindow;
|
||||||
|
[MarshalAs(UnmanagedType.LPStruct)]
|
||||||
|
public Coord dwMaximumWindowSize;
|
||||||
|
}
|
||||||
8
SrcMod/Shell/Interop/ObjectModels/Coord.cs
Normal file
8
SrcMod/Shell/Interop/ObjectModels/Coord.cs
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
namespace SrcMod.Shell.Interop.ObjectModels;
|
||||||
|
|
||||||
|
[StructLayout(LayoutKind.Sequential)]
|
||||||
|
internal struct Coord
|
||||||
|
{
|
||||||
|
public short X;
|
||||||
|
public short Y;
|
||||||
|
}
|
||||||
10
SrcMod/Shell/Interop/ObjectModels/SmallRect.cs
Normal file
10
SrcMod/Shell/Interop/ObjectModels/SmallRect.cs
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
namespace SrcMod.Shell.Interop.ObjectModels;
|
||||||
|
|
||||||
|
[StructLayout(LayoutKind.Sequential)]
|
||||||
|
internal struct SmallRect
|
||||||
|
{
|
||||||
|
public short Left;
|
||||||
|
public short Top;
|
||||||
|
public short Right;
|
||||||
|
public short Bottom;
|
||||||
|
}
|
||||||
@ -6,6 +6,7 @@ global using SharpCompress.Archives.SevenZip;
|
|||||||
global using SharpCompress.Readers;
|
global using SharpCompress.Readers;
|
||||||
global using SrcMod.Shell.Extensions;
|
global using SrcMod.Shell.Extensions;
|
||||||
global using SrcMod.Shell.Interop;
|
global using SrcMod.Shell.Interop;
|
||||||
|
global using SrcMod.Shell.Interop.ObjectModels;
|
||||||
global using SrcMod.Shell.Modules;
|
global using SrcMod.Shell.Modules;
|
||||||
global using SrcMod.Shell.Modules.ObjectModels;
|
global using SrcMod.Shell.Modules.ObjectModels;
|
||||||
global using SrcMod.Shell.ObjectModels;
|
global using SrcMod.Shell.ObjectModels;
|
||||||
|
|||||||
665
SrcMod/Shell/Modules/Valve/VkvModule.cs
Normal file
665
SrcMod/Shell/Modules/Valve/VkvModule.cs
Normal file
@ -0,0 +1,665 @@
|
|||||||
|
namespace SrcMod.Shell.Modules.Valve;
|
||||||
|
|
||||||
|
[Module("vkv")]
|
||||||
|
public static class VkvModule
|
||||||
|
{
|
||||||
|
[Command("create")]
|
||||||
|
[CanCancel(false)]
|
||||||
|
public static void CreateVkv(string path)
|
||||||
|
{
|
||||||
|
if (File.Exists(path)) throw new($"File already exists at \"{path}\". Did you mean to run \"vkv edit\"?");
|
||||||
|
|
||||||
|
VkvNode parentNode = new VkvTreeNode()
|
||||||
|
{
|
||||||
|
{ "key", new VkvSingleNode("value") }
|
||||||
|
};
|
||||||
|
string parentNodeName = "tree";
|
||||||
|
|
||||||
|
VkvModifyWhole(ref parentNode, ref parentNodeName);
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
FileStream fs = new(path, FileMode.Create);
|
||||||
|
SerializeVkv.Serialize(fs, parentNode, parentNodeName);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
#if DEBUG
|
||||||
|
throw;
|
||||||
|
#else
|
||||||
|
throw new($"Error serializing result to file: {ex.Message}");
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Command("edit")]
|
||||||
|
[CanCancel(false)]
|
||||||
|
public static void EditVkv(string path)
|
||||||
|
{
|
||||||
|
if (!File.Exists(path)) throw new($"No file exists at \"{path}\". Did you mean to run \"vkv create\"?");
|
||||||
|
|
||||||
|
VkvNode? parentNode;
|
||||||
|
string parentNodeName = string.Empty;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
FileStream fs = new(path, FileMode.Open);
|
||||||
|
parentNode = SerializeVkv.Deserialize(fs, out parentNodeName);
|
||||||
|
|
||||||
|
if (parentNode is null) throw new("Deserialized VKV node is null.");
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
#if DEBUG
|
||||||
|
throw;
|
||||||
|
#else
|
||||||
|
throw new($"Error parsing file to Valve KeyValues format: {ex.Message}");
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
VkvModifyWhole(ref parentNode, ref parentNodeName);
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
FileStream fs = new(path, FileMode.Create);
|
||||||
|
SerializeVkv.Serialize(fs, parentNode, parentNodeName);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
#if DEBUG
|
||||||
|
throw;
|
||||||
|
#else
|
||||||
|
throw new($"Error serializing result to file: {ex.Message}");
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#region The VKV Modification System
|
||||||
|
private static void VkvModifyWhole(ref VkvNode rootNode, ref string rootNodeName)
|
||||||
|
{
|
||||||
|
// Generate reference context for passing to the modification methods.
|
||||||
|
VkvModifyContext context = new()
|
||||||
|
{
|
||||||
|
displayLines = VkvModifyGetLines(rootNode, rootNodeName, 0),
|
||||||
|
rootNode = rootNode,
|
||||||
|
rootNodeName = rootNodeName
|
||||||
|
};
|
||||||
|
|
||||||
|
// Make an initial printing of the vkv node.
|
||||||
|
VkvModifyPrintAll(ref context, false);
|
||||||
|
|
||||||
|
// Start modifying the root node.
|
||||||
|
VkvTreeNode? nullNode = null;
|
||||||
|
int nullInt = default;
|
||||||
|
VkvModifyNode(ref rootNode, ref rootNodeName, ref context, true, ref nullNode, ref nullInt);
|
||||||
|
|
||||||
|
// Done editing, let's reset the cursor position and exit the command.
|
||||||
|
Console.ResetColor();
|
||||||
|
Console.SetCursorPosition(0, context.startingCursor + context.displayLines.Count);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static VkvModifyOption VkvModifyNode(ref VkvNode node, ref string nodeName,
|
||||||
|
ref VkvModifyContext context, bool isGlobal, ref VkvTreeNode? parentNode, ref int parentSubIndex)
|
||||||
|
{
|
||||||
|
#if DEBUG
|
||||||
|
string add = $" {nodeName}";
|
||||||
|
Console.Title += add;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
int subIndex = -1; // Represents the index of the sub node currently being modified.
|
||||||
|
// If the variable is set to -1, it represents the title.
|
||||||
|
|
||||||
|
VkvSingleNode? single = node as VkvSingleNode;
|
||||||
|
VkvTreeNode? tree = node as VkvTreeNode;
|
||||||
|
|
||||||
|
VkvModifyOption? option = null;
|
||||||
|
VkvModifySelection selection = VkvModifySelection.Name;
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
// Color the display white, wait for a key, then reset and handle the key press.
|
||||||
|
string line = context.displayLines[context.lineIndex];
|
||||||
|
|
||||||
|
Console.SetCursorPosition(0, context.startingCursor + context.lineIndex);
|
||||||
|
Console.Write(Whitify(line, selection));
|
||||||
|
Console.ResetColor();
|
||||||
|
|
||||||
|
if (!option.HasValue) option = Console.ReadKey(true).Key switch
|
||||||
|
{
|
||||||
|
ConsoleKey.DownArrow => VkvModifyOption.IncSubIndex,
|
||||||
|
ConsoleKey.UpArrow => VkvModifyOption.DecSubIndex,
|
||||||
|
ConsoleKey.RightArrow => VkvModifyOption.RShiftMode,
|
||||||
|
ConsoleKey.LeftArrow => VkvModifyOption.LShiftMode,
|
||||||
|
ConsoleKey.Enter => VkvModifyOption.Use,
|
||||||
|
ConsoleKey.Escape => VkvModifyOption.ExitAll,
|
||||||
|
_ => VkvModifyOption.Nothing
|
||||||
|
};
|
||||||
|
|
||||||
|
Console.CursorLeft = 0; // This is assuming the cursor hasn't moved, which it shouldn't.
|
||||||
|
Console.Write(line + new string(' ', Console.WindowWidth - line.Length));
|
||||||
|
|
||||||
|
// Now we handle the key press.
|
||||||
|
switch (option)
|
||||||
|
{
|
||||||
|
case VkvModifyOption.IncSubIndex:
|
||||||
|
if (tree is not null)
|
||||||
|
{
|
||||||
|
subIndex++;
|
||||||
|
|
||||||
|
if (subIndex == 0)
|
||||||
|
{
|
||||||
|
// We just shifted down from the title to the first sub node.
|
||||||
|
// We need to overlook the next line, '{'.
|
||||||
|
|
||||||
|
context.lineIndex += 2;
|
||||||
|
|
||||||
|
// Now we also need to start modification of the first sub node.
|
||||||
|
KeyValuePair<string, VkvNode>? subNode = tree[subIndex];
|
||||||
|
if (subNode is not null)
|
||||||
|
{
|
||||||
|
string subNodeKey = subNode.Value.Key;
|
||||||
|
VkvNode subNodeValue = subNode.Value.Value;
|
||||||
|
VkvModifyOption status =
|
||||||
|
VkvModifyNode(ref subNodeValue, ref subNodeKey, ref context, false, ref tree, ref subIndex);
|
||||||
|
|
||||||
|
// Update the parent node with our modified sub node.
|
||||||
|
tree![subIndex] = new(subNodeKey, subNodeValue);
|
||||||
|
|
||||||
|
// Set the next instruction.
|
||||||
|
option = status;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (subIndex == tree.SubNodeCount + 1)
|
||||||
|
{
|
||||||
|
// We're outside the maximum sub nodes. Let's increment the parent
|
||||||
|
// sub index and end this method.
|
||||||
|
// Incrementing the line index to overlook the next line, '}'
|
||||||
|
|
||||||
|
#if DEBUG
|
||||||
|
Console.Title = Console.Title[..^add.Length];
|
||||||
|
#endif
|
||||||
|
context.lineIndex++;
|
||||||
|
return VkvModifyOption.IncSubIndex;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// We're in a valid range. Let's just change the sub node we're
|
||||||
|
// focused on.
|
||||||
|
context.lineIndex++;
|
||||||
|
|
||||||
|
if (subIndex < tree.SubNodeCount)
|
||||||
|
{
|
||||||
|
// We are talking about an already existing node, so we also need
|
||||||
|
// to start modification of the first sub node.
|
||||||
|
KeyValuePair<string, VkvNode>? subNode = tree[subIndex];
|
||||||
|
if (subNode is not null)
|
||||||
|
{
|
||||||
|
string subNodeKey = subNode.Value.Key;
|
||||||
|
VkvNode subNodeValue = subNode.Value.Value;
|
||||||
|
|
||||||
|
VkvModifyOption status =
|
||||||
|
VkvModifyNode(ref subNodeValue, ref subNodeKey, ref context, false, ref tree, ref subIndex);
|
||||||
|
|
||||||
|
// Update the parent node with our modified sub node.
|
||||||
|
tree![subIndex] = new(subNodeKey, subNodeValue);
|
||||||
|
|
||||||
|
// Set the next instruction.
|
||||||
|
option = status;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// TODO: This is where we can decide to add sub nodes.
|
||||||
|
option = null;
|
||||||
|
selection = VkvModifySelection.CreateNew;
|
||||||
|
|
||||||
|
#if DEBUG
|
||||||
|
string secondAdd = " [CREATE NEW]";
|
||||||
|
add += secondAdd;
|
||||||
|
Console.Title += secondAdd;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// We aren't in a tree. We just change the parent sub index and
|
||||||
|
// end this method (and increment the line).
|
||||||
|
|
||||||
|
#if DEBUG
|
||||||
|
Console.Title = Console.Title[..^add.Length];
|
||||||
|
#endif
|
||||||
|
return VkvModifyOption.IncSubIndex;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case VkvModifyOption.DecSubIndex:
|
||||||
|
// TODO: Implement when moving downward is complete.
|
||||||
|
// It's a little weird to not be able to move back up,
|
||||||
|
// I know, but it's gonna be weirder to implement, and
|
||||||
|
// I only want to do it once.
|
||||||
|
option = null;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case VkvModifyOption.RShiftMode:
|
||||||
|
selection = selection switch
|
||||||
|
{
|
||||||
|
VkvModifySelection.Delete => VkvModifySelection.Name,
|
||||||
|
VkvModifySelection.Name => single is null ? selection : VkvModifySelection.Value,
|
||||||
|
_ => selection
|
||||||
|
};
|
||||||
|
option = null;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case VkvModifyOption.LShiftMode:
|
||||||
|
selection = selection switch
|
||||||
|
{
|
||||||
|
VkvModifySelection.Name => isGlobal ? VkvModifySelection.Name : VkvModifySelection.Delete,
|
||||||
|
VkvModifySelection.Value => VkvModifySelection.Name,
|
||||||
|
_ => selection
|
||||||
|
};
|
||||||
|
option = null;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case VkvModifyOption.Use:
|
||||||
|
switch (selection)
|
||||||
|
{
|
||||||
|
case VkvModifySelection.Delete:
|
||||||
|
string unrefNodeName = nodeName;
|
||||||
|
VkvNode unrefNode = node;
|
||||||
|
parentNode![parentSubIndex - 1] = null;
|
||||||
|
|
||||||
|
// Inefficient, yeah, but again, this is intended to
|
||||||
|
// be used by humans, not robots. Feel free to improve
|
||||||
|
// it if you want, but I probably won't.
|
||||||
|
|
||||||
|
List<string> newLines = VkvModifyGetLines(context.rootNode, context.rootNodeName, 0);
|
||||||
|
int endBuffer = context.displayLines.Count - newLines.Count;
|
||||||
|
context.displayLines = newLines;
|
||||||
|
|
||||||
|
VkvModifyPrintAll(ref context, true, true);
|
||||||
|
|
||||||
|
// TODO: Kinda works, but it's quite buggy. Fix later.
|
||||||
|
|
||||||
|
Console.SetCursorPosition(0, context.startingCursor + context.displayLines.Count);
|
||||||
|
for (int i = 0; i < endBuffer; i++) Console.WriteLine(new string(' ', Console.WindowWidth));
|
||||||
|
break;
|
||||||
|
|
||||||
|
case VkvModifySelection.Name:
|
||||||
|
VkvModifyRefactorName(ref nodeName, ref context);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case VkvModifySelection.Value:
|
||||||
|
if (single is null)
|
||||||
|
{
|
||||||
|
option = null;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
VkvModifyRefactorValue(ref single, ref context);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case VkvModifySelection.CreateNew:
|
||||||
|
// TODO
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
option = null;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case VkvModifyOption.ExitAll: return VkvModifyOption.ExitAll;
|
||||||
|
|
||||||
|
default:
|
||||||
|
option = null;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void VkvModifyRefactorName(ref string nodeName, ref VkvModifyContext context)
|
||||||
|
{
|
||||||
|
string originalName = nodeName;
|
||||||
|
string edit = context.displayLines[context.lineIndex];
|
||||||
|
|
||||||
|
int firstQuote = edit.IndexOf('\"'),
|
||||||
|
secondQuote = edit[(firstQuote + 1)..].IndexOf('\"') + firstQuote + 1;
|
||||||
|
|
||||||
|
int displayIndex = secondQuote, nameIndex = nodeName.Length;
|
||||||
|
|
||||||
|
int additionalAnsiTakeoff = 0;
|
||||||
|
|
||||||
|
{
|
||||||
|
// I almost never do this ("this" being the brackets while
|
||||||
|
// already inside a method), but I also don't like
|
||||||
|
// keeping temporary variables around for no reason.
|
||||||
|
bool tempActive = false;
|
||||||
|
for (int i = 0; i < displayIndex; i++)
|
||||||
|
{
|
||||||
|
if (edit[i] == '\x1b') tempActive = true;
|
||||||
|
if (tempActive) additionalAnsiTakeoff++;
|
||||||
|
if (tempActive && edit[i] == 'm') tempActive = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
Console.CursorLeft = 0;
|
||||||
|
Console.Write(Whitify(edit, VkvModifySelection.Name, true));
|
||||||
|
|
||||||
|
Console.CursorLeft = displayIndex - additionalAnsiTakeoff;
|
||||||
|
Console.CursorVisible = true;
|
||||||
|
ConsoleKeyInfo key = Console.ReadKey(true);
|
||||||
|
Console.CursorVisible = false;
|
||||||
|
if (char.IsControl(key.KeyChar))
|
||||||
|
{
|
||||||
|
// TODO: Adding clipboard support might be cool (but also a pain).
|
||||||
|
bool end = false;
|
||||||
|
switch (key.Key)
|
||||||
|
{
|
||||||
|
case ConsoleKey.Backspace:
|
||||||
|
if (nameIndex > 0)
|
||||||
|
{
|
||||||
|
nodeName = nodeName.Remove(nameIndex - 1, 1);
|
||||||
|
edit = edit.Remove(displayIndex - 1, 1) + ' ';
|
||||||
|
displayIndex--;
|
||||||
|
nameIndex--;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case ConsoleKey.Delete:
|
||||||
|
if (nameIndex < nodeName.Length)
|
||||||
|
{
|
||||||
|
nodeName = nodeName.Remove(nameIndex, 1);
|
||||||
|
edit = edit.Remove(displayIndex, 1) + ' ';
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case ConsoleKey.Escape:
|
||||||
|
nodeName = originalName;
|
||||||
|
edit = context.displayLines[context.lineIndex];
|
||||||
|
end = true;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case ConsoleKey.Enter:
|
||||||
|
end = true;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case ConsoleKey.LeftArrow:
|
||||||
|
if (nameIndex > 0)
|
||||||
|
{
|
||||||
|
displayIndex--;
|
||||||
|
nameIndex--;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case ConsoleKey.RightArrow:
|
||||||
|
if (nameIndex < nodeName.Length)
|
||||||
|
{
|
||||||
|
displayIndex++;
|
||||||
|
nameIndex++;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (end) break;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
nodeName = nodeName.Insert(nameIndex, key.KeyChar.ToString());
|
||||||
|
edit = edit.Insert(displayIndex, key.KeyChar.ToString()).TrimEnd();
|
||||||
|
displayIndex++;
|
||||||
|
nameIndex++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
context.displayLines[context.lineIndex] = edit.TrimEnd();
|
||||||
|
}
|
||||||
|
private static void VkvModifyRefactorValue(ref VkvSingleNode node, ref VkvModifyContext context)
|
||||||
|
{
|
||||||
|
string value = node.value?.ToString() ?? string.Empty,
|
||||||
|
edit = context.displayLines[context.lineIndex];
|
||||||
|
|
||||||
|
int firstQuote = edit.IndexOf('\"'),
|
||||||
|
secondQuote = edit[(firstQuote + 1)..].IndexOf('\"') + firstQuote + 1,
|
||||||
|
thirdQuote = edit[(secondQuote + 1)..].IndexOf('\"') + secondQuote + 1,
|
||||||
|
fourthQuote = edit[(thirdQuote + 1)..].IndexOf('\"') + thirdQuote + 1;
|
||||||
|
|
||||||
|
int displayIndex = fourthQuote, valueIndex = value.Length;
|
||||||
|
|
||||||
|
int additionalAnsiTakeoff = 0;
|
||||||
|
|
||||||
|
{
|
||||||
|
// See my opinions of brackets while already
|
||||||
|
// inside a method in the `VkvModifyRefactorName`
|
||||||
|
// method.
|
||||||
|
bool tempActive = false;
|
||||||
|
for (int i = 0; i < displayIndex; i++)
|
||||||
|
{
|
||||||
|
if (edit[i] == '\x1b') tempActive = true;
|
||||||
|
if (tempActive) additionalAnsiTakeoff++;
|
||||||
|
if (tempActive && edit[i] == 'm') tempActive = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
Console.CursorLeft = 0;
|
||||||
|
Console.Write(Whitify(edit, VkvModifySelection.Value, true));
|
||||||
|
|
||||||
|
Console.CursorLeft = displayIndex - additionalAnsiTakeoff;
|
||||||
|
Console.CursorVisible = true;
|
||||||
|
ConsoleKeyInfo key = Console.ReadKey(true);
|
||||||
|
Console.CursorVisible = false;
|
||||||
|
if (char.IsControl(key.KeyChar))
|
||||||
|
{
|
||||||
|
// TODO: Adding clipboard support might be cool (but also a pain).
|
||||||
|
bool end = false;
|
||||||
|
switch (key.Key)
|
||||||
|
{
|
||||||
|
case ConsoleKey.Backspace:
|
||||||
|
if (valueIndex > 0)
|
||||||
|
{
|
||||||
|
value = value.Remove(valueIndex - 1, 1);
|
||||||
|
edit = edit.Remove(displayIndex - 1, 1) + ' ';
|
||||||
|
displayIndex--;
|
||||||
|
valueIndex--;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case ConsoleKey.Delete:
|
||||||
|
if (valueIndex < value.Length)
|
||||||
|
{
|
||||||
|
value = value.Remove(valueIndex, 1);
|
||||||
|
edit = edit.Remove(displayIndex, 1) + ' ';
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case ConsoleKey.Escape:
|
||||||
|
edit = context.displayLines[context.lineIndex];
|
||||||
|
end = true;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case ConsoleKey.Enter:
|
||||||
|
node.value = value;
|
||||||
|
end = true;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case ConsoleKey.LeftArrow:
|
||||||
|
if (valueIndex > 0)
|
||||||
|
{
|
||||||
|
displayIndex--;
|
||||||
|
valueIndex--;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case ConsoleKey.RightArrow:
|
||||||
|
if (valueIndex < value.Length)
|
||||||
|
{
|
||||||
|
displayIndex++;
|
||||||
|
valueIndex++;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (end) break;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
value = value.Insert(valueIndex, key.KeyChar.ToString());
|
||||||
|
edit = edit.Insert(displayIndex, key.KeyChar.ToString()).TrimEnd();
|
||||||
|
displayIndex++;
|
||||||
|
valueIndex++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
context.displayLines[context.lineIndex] = edit.TrimEnd();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void VkvModifyPrintAll(ref VkvModifyContext context, bool resetCursor, bool flushLine = false)
|
||||||
|
{
|
||||||
|
Int2 cursorPos = (Console.CursorLeft, Console.CursorTop);
|
||||||
|
|
||||||
|
Console.SetCursorPosition(0, context.startingCursor);
|
||||||
|
foreach (string line in context.displayLines)
|
||||||
|
{
|
||||||
|
Console.Write(line);
|
||||||
|
if (flushLine) Console.Write(new string(' ', Console.WindowWidth - line.Length));
|
||||||
|
Console.WriteLine();
|
||||||
|
Console.ResetColor();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (resetCursor) Console.SetCursorPosition(cursorPos.x, cursorPos.y);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static List<string> VkvModifyGetLines(VkvNode node, string nodeName, int indent)
|
||||||
|
{
|
||||||
|
int spaceCount = indent * 4,
|
||||||
|
nextSpaceCount = (indent + 1) * 4;
|
||||||
|
|
||||||
|
List<string> lines = new();
|
||||||
|
|
||||||
|
if (node is VkvSingleNode single) lines.Add(new string(' ', spaceCount) + $"\x1b[33m\"{nodeName}\"" +
|
||||||
|
$" \x1b[32m\"{single.value}\"");
|
||||||
|
else if (node is VkvTreeNode tree)
|
||||||
|
{
|
||||||
|
lines.Add(new string(' ', spaceCount) + $"\x1b[94m\"{nodeName}\"");
|
||||||
|
lines.Add(new string(' ', spaceCount) + "{");
|
||||||
|
foreach (KeyValuePair<string, VkvNode> pair in tree)
|
||||||
|
{
|
||||||
|
lines.AddRange(VkvModifyGetLines(pair.Value, pair.Key, indent + 1));
|
||||||
|
}
|
||||||
|
lines.Add(new string(' ', nextSpaceCount) + "\x1b[35m...");
|
||||||
|
lines.Add(new string(' ', spaceCount) + "}");
|
||||||
|
}
|
||||||
|
else lines.Add(new string(' ', spaceCount) + "\x1b[31mError");
|
||||||
|
|
||||||
|
return lines;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string Whitify(string content, VkvModifySelection selection, bool blink = false)
|
||||||
|
{
|
||||||
|
StringBuilder result = new();
|
||||||
|
|
||||||
|
// This is definitely optimizable, but I don't feel like doing that yet.
|
||||||
|
// Maybe in the future.
|
||||||
|
// For future reference, when (if) this is optimized, I am doing stuff like this in this
|
||||||
|
// method along with the name and value refactoring methods.
|
||||||
|
int firstQuote = content.IndexOf('\"'),
|
||||||
|
secondQuote = content[(firstQuote + 1)..].IndexOf('\"') + firstQuote + 1,
|
||||||
|
thirdQuote = content[(secondQuote + 1)..].IndexOf('\"') + secondQuote + 1,
|
||||||
|
fourthQuote = content[(thirdQuote + 1)..].IndexOf('\"') + thirdQuote + 1;
|
||||||
|
|
||||||
|
int startChar = 0;
|
||||||
|
while (char.IsWhiteSpace(content[startChar])) startChar++;
|
||||||
|
|
||||||
|
int endChar = content.Length - 1;
|
||||||
|
while (char.IsWhiteSpace(content[endChar])) endChar--;
|
||||||
|
|
||||||
|
switch (selection)
|
||||||
|
{
|
||||||
|
case VkvModifySelection.Name:
|
||||||
|
if (firstQuote < 0 || secondQuote < 0) return content;
|
||||||
|
|
||||||
|
result.Append(content[..firstQuote]);
|
||||||
|
if (blink)
|
||||||
|
{
|
||||||
|
result.Append("\"\x1b[5m");
|
||||||
|
result.Append(content[(firstQuote + 1)..secondQuote]);
|
||||||
|
result.Append("\x1b[25m\"");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
result.Append("\x1b[107m");
|
||||||
|
result.Append(content[firstQuote..(secondQuote + 1)]);
|
||||||
|
result.Append("\x1b[0m");
|
||||||
|
}
|
||||||
|
result.Append(content[(secondQuote + 1)..]);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case VkvModifySelection.Value:
|
||||||
|
if (thirdQuote < 0 || fourthQuote < 0) return content;
|
||||||
|
|
||||||
|
result.Append(content[..thirdQuote]);
|
||||||
|
if (blink)
|
||||||
|
{
|
||||||
|
result.Append("\"\x1b[5m");
|
||||||
|
result.Append(content[(thirdQuote + 1)..fourthQuote]);
|
||||||
|
result.Append("\x1b[25m\"");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
result.Append("\x1b[107m");
|
||||||
|
result.Append(content[thirdQuote..(fourthQuote + 1)]);
|
||||||
|
result.Append("\x1b[0m");
|
||||||
|
}
|
||||||
|
result.Append(content[(fourthQuote + 1)..]);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case VkvModifySelection.Delete:
|
||||||
|
const string addDelete = "[Delete]";
|
||||||
|
|
||||||
|
result.Append($"\x1b[107m\x1b[31m{addDelete}\x1b[0m ");
|
||||||
|
if (addDelete.Length + 1 > startChar) result.Append(content[startChar..]);
|
||||||
|
else result.Append(content[(addDelete.Length + 1)..]);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case VkvModifySelection.CreateNew:
|
||||||
|
result.Append(content[..startChar]);
|
||||||
|
result.Append("\x1b[107m");
|
||||||
|
result.Append(content[startChar..(endChar + 1)]);
|
||||||
|
result.Append("\x1b[0m");
|
||||||
|
result.Append(content[(endChar + 1)..]);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return result.ToString();
|
||||||
|
}
|
||||||
|
|
||||||
|
private class VkvModifyContext
|
||||||
|
{
|
||||||
|
public required List<string> displayLines;
|
||||||
|
public int lineIndex;
|
||||||
|
public required VkvNode rootNode;
|
||||||
|
public required string rootNodeName;
|
||||||
|
public readonly int startingCursor;
|
||||||
|
|
||||||
|
public VkvModifyContext()
|
||||||
|
{
|
||||||
|
lineIndex = 0;
|
||||||
|
startingCursor = Console.CursorTop;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private enum VkvModifyOption
|
||||||
|
{
|
||||||
|
Nothing,
|
||||||
|
IncSubIndex,
|
||||||
|
DecSubIndex,
|
||||||
|
RShiftMode,
|
||||||
|
LShiftMode,
|
||||||
|
Use,
|
||||||
|
ExitAll
|
||||||
|
}
|
||||||
|
private enum VkvModifySelection
|
||||||
|
{
|
||||||
|
Delete,
|
||||||
|
Name,
|
||||||
|
Value,
|
||||||
|
CreateNew
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
}
|
||||||
@ -4,7 +4,7 @@ public class Shell
|
|||||||
{
|
{
|
||||||
public const string Author = "That_One_Nerd";
|
public const string Author = "That_One_Nerd";
|
||||||
public const string Name = "SrcMod";
|
public const string Name = "SrcMod";
|
||||||
public const string Version = "Beta 0.5.0";
|
public const string Version = "Beta 0.6.0";
|
||||||
|
|
||||||
public bool HasAnyDisplayableError => HasDisplayableError || Config.HasDisplayableError;
|
public bool HasAnyDisplayableError => HasDisplayableError || Config.HasDisplayableError;
|
||||||
public bool HasAnyDisplayableWarning => HasDisplayableWarning || Config.HasDisplayableWarning;
|
public bool HasAnyDisplayableWarning => HasDisplayableWarning || Config.HasDisplayableWarning;
|
||||||
|
|||||||
@ -19,16 +19,19 @@ public static class VkvConvert
|
|||||||
|
|
||||||
#region DeserializeNode
|
#region DeserializeNode
|
||||||
public static VkvNode? DeserializeNode(StreamReader reader) =>
|
public static VkvNode? DeserializeNode(StreamReader reader) =>
|
||||||
DeserializeNode(reader, VkvOptions.Default);
|
DeserializeNode(reader, VkvOptions.Default, out _);
|
||||||
public static VkvNode? DeserializeNode(StreamReader reader, VkvOptions options)
|
public static VkvNode? DeserializeNode(StreamReader reader, VkvOptions options) =>
|
||||||
|
DeserializeNode(reader, options, out _);
|
||||||
|
public static VkvNode? DeserializeNode(StreamReader reader, VkvOptions options, out string name)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
return DeserializeNode(reader, options, out _, null);
|
return DeserializeNode(reader, options, out name, null);
|
||||||
}
|
}
|
||||||
catch
|
catch
|
||||||
{
|
{
|
||||||
if (!options.noExceptions) throw;
|
if (!options.noExceptions) throw;
|
||||||
|
name = string.Empty;
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -63,7 +66,9 @@ public static class VkvConvert
|
|||||||
string? current;
|
string? current;
|
||||||
while ((current = reader.ReadLine()?.Trim()) is not null)
|
while ((current = reader.ReadLine()?.Trim()) is not null)
|
||||||
{
|
{
|
||||||
if (current == "}") break;
|
if (string.IsNullOrWhiteSpace(current)) continue;
|
||||||
|
else if (current == "}") break;
|
||||||
|
|
||||||
VkvNode? output = DeserializeNode(reader, options, out string subName, current);
|
VkvNode? output = DeserializeNode(reader, options, out string subName, current);
|
||||||
tree[subName] = output;
|
tree[subName] = output;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -29,6 +29,23 @@ public class VkvSerializer
|
|||||||
if (!p_options.closeWhenFinished && p_options.resetStreamPosition) stream.Seek(pos, SeekOrigin.Begin);
|
if (!p_options.closeWhenFinished && p_options.resetStreamPosition) stream.Seek(pos, SeekOrigin.Begin);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
public VkvNode? Deserialize(Stream stream, out string name)
|
||||||
|
{
|
||||||
|
long pos = stream.Position;
|
||||||
|
StreamReader reader = new(stream, leaveOpen: !p_options.closeWhenFinished);
|
||||||
|
try
|
||||||
|
{
|
||||||
|
VkvNode? result = VkvConvert.DeserializeNode(reader, p_options, out name);
|
||||||
|
reader.Close();
|
||||||
|
if (!p_options.closeWhenFinished && p_options.resetStreamPosition) stream.Seek(pos, SeekOrigin.Begin);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
reader.Close();
|
||||||
|
if (!p_options.closeWhenFinished && p_options.resetStreamPosition) stream.Seek(pos, SeekOrigin.Begin);
|
||||||
|
}
|
||||||
|
}
|
||||||
public T? Deserialize<T>(Stream stream)
|
public T? Deserialize<T>(Stream stream)
|
||||||
{
|
{
|
||||||
VkvNode? result = Deserialize(stream);
|
VkvNode? result = Deserialize(stream);
|
||||||
|
|||||||
@ -1,45 +1,166 @@
|
|||||||
namespace Valve.Vkv;
|
namespace Valve.Vkv;
|
||||||
|
|
||||||
public class VkvTreeNode : VkvNode, IEnumerable<KeyValuePair<string, VkvNode?>>
|
public class VkvTreeNode : VkvNode, IEnumerable<KeyValuePair<string, VkvNode>>
|
||||||
{
|
{
|
||||||
public int SubNodeCount => p_subNodes.Count;
|
public int SubNodeCount => p_subNodes.Count;
|
||||||
|
|
||||||
private readonly Dictionary<string, VkvNode?> p_subNodes;
|
// These should never get out of sync, or bad things will happen.
|
||||||
|
private readonly List<string> p_subNodeKeys;
|
||||||
|
private readonly List<VkvNode> p_subNodes;
|
||||||
|
|
||||||
public VkvTreeNode(Dictionary<string, VkvNode?>? subNodes = null) : base()
|
public VkvTreeNode(Dictionary<string, VkvNode?>? subNodes = null) : base()
|
||||||
{
|
{
|
||||||
p_subNodes = subNodes ?? new();
|
p_subNodeKeys = new();
|
||||||
|
p_subNodes = new();
|
||||||
|
|
||||||
|
if (subNodes is not null)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < subNodes.Count; i++)
|
||||||
|
{
|
||||||
|
string key = subNodes.Keys.ElementAt(i);
|
||||||
|
VkvNode? value = subNodes.Values.ElementAt(i);
|
||||||
|
|
||||||
|
if (value is not null)
|
||||||
|
{
|
||||||
|
p_subNodeKeys.Add(key);
|
||||||
|
p_subNodes.Add(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public VkvNode? this[string key]
|
public VkvNode? this[string key]
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
if (p_subNodes.TryGetValue(key, out VkvNode? value)) return value;
|
int index = p_subNodeKeys.IndexOf(key);
|
||||||
else return null;
|
|
||||||
|
if (index == -1) return null;
|
||||||
|
else return p_subNodes[index];
|
||||||
}
|
}
|
||||||
set
|
set
|
||||||
{
|
{
|
||||||
if (p_subNodes.ContainsKey(key)) p_subNodes[key] = value;
|
int index = p_subNodeKeys.IndexOf(key);
|
||||||
else p_subNodes.Add(key, value);
|
|
||||||
|
if (index == -1)
|
||||||
|
{
|
||||||
|
if (value is null) return;
|
||||||
|
|
||||||
|
p_subNodeKeys.Add(key);
|
||||||
|
p_subNodes.Add(value);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (value is null)
|
||||||
|
{
|
||||||
|
p_subNodeKeys.RemoveAt(index);
|
||||||
|
p_subNodes.RemoveAt(index);
|
||||||
|
}
|
||||||
|
else p_subNodes[index] = value;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
public VkvNode? this[int index]
|
public KeyValuePair<string, VkvNode>? this[int index]
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
if (p_subNodes.Count >= index || index < 0) return null;
|
if (index >= SubNodeCount || index < 0) return null;
|
||||||
return p_subNodes.Values.ElementAt(index);
|
return new(p_subNodeKeys[index], p_subNodes[index]);
|
||||||
}
|
}
|
||||||
set
|
set
|
||||||
{
|
{
|
||||||
if (p_subNodes.Count >= index || index < 0) throw new IndexOutOfRangeException();
|
if (index >= SubNodeCount || index < 0) throw new IndexOutOfRangeException();
|
||||||
p_subNodes[p_subNodes.Keys.ElementAt(index)] = value;
|
|
||||||
|
if (value is null)
|
||||||
|
{
|
||||||
|
p_subNodeKeys.RemoveAt(index);
|
||||||
|
p_subNodes.RemoveAt(index);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
p_subNodeKeys[index] = value.Value.Key;
|
||||||
|
p_subNodes[index] = value.Value.Value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public KeyValuePair<string, VkvNode>? this[Func<int, KeyValuePair<string, VkvNode>, bool> predicate]
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
for (int i = 0; i < SubNodeCount; i++)
|
||||||
|
{
|
||||||
|
KeyValuePair<string, VkvNode> pair = new(p_subNodeKeys[i], p_subNodes[i]);
|
||||||
|
if (predicate(i, pair)) return pair;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
set
|
||||||
|
{
|
||||||
|
for (int i = 0; i < SubNodeCount; i++)
|
||||||
|
{
|
||||||
|
KeyValuePair<string, VkvNode> pair = new(p_subNodeKeys[i], p_subNodes[i]);
|
||||||
|
if (predicate(i, pair))
|
||||||
|
{
|
||||||
|
if (value.HasValue)
|
||||||
|
{
|
||||||
|
p_subNodeKeys[i] = value.Value.Key;
|
||||||
|
p_subNodes[i] = value.Value.Value;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
p_subNodeKeys.RemoveAt(i);
|
||||||
|
p_subNodes.RemoveAt(i);
|
||||||
|
i--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Add(string key, VkvNode? value) => this[key] = value;
|
public void Add(string key, VkvNode value) => this[key] = value;
|
||||||
|
|
||||||
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
|
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
|
||||||
public IEnumerator<KeyValuePair<string, VkvNode?>> GetEnumerator() => p_subNodes.GetEnumerator();
|
public IEnumerator<KeyValuePair<string, VkvNode>> GetEnumerator()
|
||||||
|
{
|
||||||
|
for (int i = 0; i < SubNodeCount; i++)
|
||||||
|
{
|
||||||
|
yield return new(p_subNodeKeys[i], p_subNodes[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Remove(string key)
|
||||||
|
{
|
||||||
|
int index = p_subNodeKeys.IndexOf(key);
|
||||||
|
if (index == -1) return;
|
||||||
|
|
||||||
|
p_subNodeKeys.RemoveAt(index);
|
||||||
|
p_subNodes.RemoveAt(index);
|
||||||
|
}
|
||||||
|
public void Remove(VkvNode value)
|
||||||
|
{
|
||||||
|
int index = p_subNodes.IndexOf(value);
|
||||||
|
if (index == -1) return;
|
||||||
|
|
||||||
|
p_subNodeKeys.RemoveAt(index);
|
||||||
|
p_subNodes.RemoveAt(index);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void RemoveAll(string key)
|
||||||
|
{
|
||||||
|
int index;
|
||||||
|
while ((index = p_subNodeKeys.IndexOf(key)) != -1)
|
||||||
|
{
|
||||||
|
p_subNodeKeys.RemoveAt(index);
|
||||||
|
p_subNodes.RemoveAt(index);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public void RemoveAll(VkvNode value)
|
||||||
|
{
|
||||||
|
int index;
|
||||||
|
while ((index = p_subNodes.IndexOf(value)) != -1)
|
||||||
|
{
|
||||||
|
p_subNodeKeys.RemoveAt(index);
|
||||||
|
p_subNodes.RemoveAt(index);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user