Compare commits
1 Commits
vdf-format
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| f479f63662 |
@ -2,17 +2,10 @@
|
|||||||
|
|
||||||
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", SetLastError = true)]
|
[LibraryImport("kernel32.dll")]
|
||||||
public static partial nuint GlobalSize(nint hPtr);
|
public static partial nuint GlobalSize(nint hPtr);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,15 +0,0 @@
|
|||||||
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;
|
|
||||||
}
|
|
||||||
@ -1,8 +0,0 @@
|
|||||||
namespace SrcMod.Shell.Interop.ObjectModels;
|
|
||||||
|
|
||||||
[StructLayout(LayoutKind.Sequential)]
|
|
||||||
internal struct Coord
|
|
||||||
{
|
|
||||||
public short X;
|
|
||||||
public short Y;
|
|
||||||
}
|
|
||||||
@ -1,10 +0,0 @@
|
|||||||
namespace SrcMod.Shell.Interop.ObjectModels;
|
|
||||||
|
|
||||||
[StructLayout(LayoutKind.Sequential)]
|
|
||||||
internal struct SmallRect
|
|
||||||
{
|
|
||||||
public short Left;
|
|
||||||
public short Top;
|
|
||||||
public short Right;
|
|
||||||
public short Bottom;
|
|
||||||
}
|
|
||||||
@ -6,7 +6,6 @@ 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;
|
||||||
|
|||||||
@ -1,665 +0,0 @@
|
|||||||
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.6.0";
|
public const string Version = "Beta 0.5.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,19 +19,16 @@ public static class VkvConvert
|
|||||||
|
|
||||||
#region DeserializeNode
|
#region DeserializeNode
|
||||||
public static VkvNode? DeserializeNode(StreamReader reader) =>
|
public static VkvNode? DeserializeNode(StreamReader reader) =>
|
||||||
DeserializeNode(reader, VkvOptions.Default, out _);
|
DeserializeNode(reader, VkvOptions.Default);
|
||||||
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 name, null);
|
return DeserializeNode(reader, options, out _, null);
|
||||||
}
|
}
|
||||||
catch
|
catch
|
||||||
{
|
{
|
||||||
if (!options.noExceptions) throw;
|
if (!options.noExceptions) throw;
|
||||||
name = string.Empty;
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -66,9 +63,7 @@ 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 (string.IsNullOrWhiteSpace(current)) continue;
|
if (current == "}") break;
|
||||||
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,23 +29,6 @@ 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,166 +1,45 @@
|
|||||||
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;
|
||||||
|
|
||||||
// These should never get out of sync, or bad things will happen.
|
private readonly Dictionary<string, VkvNode?> p_subNodes;
|
||||||
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_subNodeKeys = new();
|
p_subNodes = subNodes ?? 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
|
||||||
{
|
{
|
||||||
int index = p_subNodeKeys.IndexOf(key);
|
if (p_subNodes.TryGetValue(key, out VkvNode? value)) return value;
|
||||||
|
else return null;
|
||||||
if (index == -1) return null;
|
|
||||||
else return p_subNodes[index];
|
|
||||||
}
|
}
|
||||||
set
|
set
|
||||||
{
|
{
|
||||||
int index = p_subNodeKeys.IndexOf(key);
|
if (p_subNodes.ContainsKey(key)) p_subNodes[key] = value;
|
||||||
|
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 (index >= SubNodeCount || index < 0) return null;
|
if (p_subNodes.Count >= index || index < 0) return null;
|
||||||
return new(p_subNodeKeys[index], p_subNodes[index]);
|
return p_subNodes.Values.ElementAt(index);
|
||||||
}
|
}
|
||||||
set
|
set
|
||||||
{
|
{
|
||||||
if (index >= SubNodeCount || index < 0) throw new IndexOutOfRangeException();
|
if (p_subNodes.Count >= index || 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()
|
public IEnumerator<KeyValuePair<string, VkvNode?>> GetEnumerator() => p_subNodes.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