From 1d488ece1446fd219f588f075f67667904cf607b Mon Sep 17 00:00:00 2001 From: "Mr. Nikes" Date: Sun, 13 Jan 2019 21:15:21 +0200 Subject: [PATCH] -add: init project --- .gitignore | 2 + XLPakTool.sln | 25 ++ XLPakTool/App.config | 6 + XLPakTool/Program.cs | 394 +++++++++++++++++++++++++++ XLPakTool/Properties/AssemblyInfo.cs | 36 +++ XLPakTool/XLPack.cs | 105 +++++++ XLPakTool/XLPakTool.csproj | 59 ++++ 7 files changed, 627 insertions(+) create mode 100644 .gitignore create mode 100644 XLPakTool.sln create mode 100644 XLPakTool/App.config create mode 100644 XLPakTool/Program.cs create mode 100644 XLPakTool/Properties/AssemblyInfo.cs create mode 100644 XLPakTool/XLPack.cs create mode 100644 XLPakTool/XLPakTool.csproj diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..e989c02 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +XLPakTool/bin/ +XLPakTool/obj/ diff --git a/XLPakTool.sln b/XLPakTool.sln new file mode 100644 index 0000000..30b6cfd --- /dev/null +++ b/XLPakTool.sln @@ -0,0 +1,25 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 15 +VisualStudioVersion = 15.0.28010.0 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "XLPakTool", "XLPakTool\XLPakTool.csproj", "{30356FF6-4A19-4B70-A776-D69D2167238A}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {30356FF6-4A19-4B70-A776-D69D2167238A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {30356FF6-4A19-4B70-A776-D69D2167238A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {30356FF6-4A19-4B70-A776-D69D2167238A}.Release|Any CPU.ActiveCfg = Release|Any CPU + {30356FF6-4A19-4B70-A776-D69D2167238A}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {C53225D8-FED9-4A94-B940-7FCB7F8DAEFD} + EndGlobalSection +EndGlobal diff --git a/XLPakTool/App.config b/XLPakTool/App.config new file mode 100644 index 0000000..731f6de --- /dev/null +++ b/XLPakTool/App.config @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/XLPakTool/Program.cs b/XLPakTool/Program.cs new file mode 100644 index 0000000..0ff480b --- /dev/null +++ b/XLPakTool/Program.cs @@ -0,0 +1,394 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.IO; +using System.Runtime.InteropServices; +using System.Threading; +using System.Threading.Tasks; + +namespace XLPakTool +{ + public class TreeDictionary + { + public string Path { get; set; } + public TreeDictionary Parent { get; set; } + public List Directories { get; set; } + public List Files { get; set; } + + public TreeDictionary(string path) + { + Path = path; + Directories = new List(); + Files = new List(); + } + } + + class Program + { + public static string GlobalPath = "/master/"; + public static string FsPath; + + static void Main(string[] args) + { + if (args.Length == 0) + { + Console.WriteLine("Start: XLPakTool.exe "); + return; + } + + var gamePakPath = args[0]; + + Log("Info", "Create file system..."); + if (XLPack.CreateFileSystem()) + { + Log("Info", "Done"); + + Log("Info", "Connect log handler..."); + XLPack.SetFileLogHandler("pack.log", LogHandler); + Log("Info", "Done"); + + MountFileSystem(gamePakPath); + Thread.Sleep(1000); + + while (true) + { + Console.Write($"~{GlobalPath}$ "); + var command = Console.ReadLine(); + var parse = CommandParser(command); + + if (parse.Length > 0) + { + var cmd = parse[0]; + var cmdArgs = new string[parse.Length - 1]; + if (cmdArgs.Length > 0) + Array.Copy(parse, 1, cmdArgs, 0, cmdArgs.Length); + + if (cmd == "quit") + break; + else + { + switch (cmd) + { + case "help": + Log("Info", "cd -> move at folders"); + Log("Info", "ls -> get files"); + Log("Info", "cp -> copy file from src to dest"); + Log("Info", "rm -> remove path"); + Console.WriteLine("--------------------------------"); + Log("Info", "To export file(s)/dir:"); + Log("Info", "cp /fs/"); + Log("Info", "To import file(s)/dir:"); + Log("Info", "cp /fs/ "); + break; + case "cd": + if (cmdArgs.Length == 0) + Log("Info", "cd "); + else + { + var cmdPath = cmdArgs[0]; + var prePath = GlobalPath; + GlobalPath = AbsolutePath(cmdPath); + if (!GlobalPath.EndsWith("/") && !GlobalPath.EndsWith("\\")) + GlobalPath += "/"; + + if (!IsDirectory(GlobalPath)) + GlobalPath = prePath; + } + break; + case "ls": + var path = GlobalPath; + + if (cmdArgs.Length > 0) + { + path = AbsolutePath(cmdArgs[0]); + path += "/"; + } + + var files = GetFiles(path); + if (files.Count > 0) + foreach (var file in files) + { + if (IsDirectory(file)) + Console.BackgroundColor = ConsoleColor.Blue; + Console.WriteLine(file.Replace(path, "")); + Console.ResetColor(); + } + else + Console.WriteLine("------ EMPTY ------"); + break; + case "cp": + if (cmdArgs.Length < 2) + Log("Info", "cp "); + else + { + var src = AbsolutePath(cmdArgs[0]); + var dest = AbsolutePath(cmdArgs[1]); + + var exist = false; + + if (src.StartsWith("/fs")) + { + var realyPath = src.Replace("/fs", FsPath); + exist = File.Exists(realyPath); + if (!exist) + exist = Directory.Exists(realyPath); + } + else + exist = IsPathExist(src); + + Thread.Sleep(1000); + + if (dest.StartsWith("/fs")) + { + var realyPath = dest.Replace("/fs", FsPath); + Directory.CreateDirectory(realyPath); + } + + if (!exist) + Log("Warn", "Bad source path: {0}", src); + else + { + var result = false; + if (IsDirectory(src)) + result = XLPack.CopyDir(src, dest); + else + result = XLPack.Copy(src, dest); + + if (result) + Console.WriteLine("Done"); + else + Console.WriteLine("Copy failed..."); + } + } + break; + case "rm": + if (cmdArgs.Length == 0) + Log("Info", "rm "); + else + { + path = AbsolutePath(cmdArgs[0]); + if (IsDirectory(path)) + { + if (XLPack.DeleteDir(path)) + Console.WriteLine("Done"); + else + Console.WriteLine("Remove failed..."); + } + else + { + if (XLPack.FDelete(path)) + Console.WriteLine("Done"); + else + Console.WriteLine("Remove failed..."); + } + } + break; + } + } + } + } + + Destroy(); + } + else + Log("Error", "Cannot create file system"); + } + + private static void LogHandler(params string[] p) + { + foreach (string str in p) + Console.WriteLine(str); + } + + private static void MountFileSystem(string path) + { + var pack = new FileInfo(path); + + FsPath = pack.DirectoryName; + + Log("Info", "Mount /fs ..."); + XLPack.Mount("/fs", FsPath, true); + Log("Info", "Done"); + + Log("Info", "Mount /master ..."); + XLPack.Mount("/master", path, true); + Log("Info", "Done"); + } + + public static async Task GetFileSystemStruct(TreeDictionary master) + { + var files = await GetFilesAsync(master.Path + "/"); + foreach (var file in files) + { + if (await IsDirectoryAsync(file)) + { + var folder = new TreeDictionary(file); + folder.Parent = master; + await GetFileSystemStruct(folder); + master.Directories.Add(folder); + } + else + master.Files.Add(file); + } + return master; + } + + private static Task> GetFilesAsync(string path) + { + return Task.FromResult(GetFiles(path)); + } + + private static List GetFiles(string path) + { + var result = new List(); + + var file = path + "*"; + XLPack.afs_finddata fd = new XLPack.afs_finddata(); + int findHandle = XLPack.FindFirst(path, ref fd); + if (findHandle != -1) + { + do + { + string stringAnsi = Marshal.PtrToStringAnsi(XLPack.GetFileName(ref fd)); + result.Add(path + stringAnsi); + } + while (XLPack.FindNext(findHandle, ref fd) != -1); + } + XLPack.FindClose(findHandle); + return result; + } + + private static bool IsDirectory(string path) + { + if (XLPack.IsFileExist(path.ToCharArray())) + return false; + XLPack.afs_finddata fd = new XLPack.afs_finddata(); + int first = XLPack.FindFirst(path, ref fd); + bool flag = first != -1; + XLPack.FindClose(first); + return flag; + } + + private static Task IsDirectoryAsync(string path) + { + return Task.FromResult(IsDirectory(path)); + } + + private static bool IsPathExist(string path) + { + if (XLPack.IsFileExist(path.ToCharArray())) + return true; + XLPack.afs_finddata fd = new XLPack.afs_finddata(); + int first = XLPack.FindFirst(path, ref fd); + var exist = first != -1; + XLPack.FindClose(first); + return exist; + } + + private static bool Copy(string from, string to) + { + if (IsDirectory(from)) + return XLPack.CopyDir(from, to); + return XLPack.Copy(from, to); + } + + private static Task CopyAsync(string from, string to) + { + return Task.FromResult(Copy(from, to)); + } + + private static void Destroy() + { + DestroyFileSystem(); + XLPack.DestroyFileLogHandler(IntPtr.Zero); + XLPack.DestroyFileSystem(); + } + + private static void DestroyFileSystem() + { + XLPack.Unmount("/master"); + XLPack.Unmount("/fs"); + } + + public static string[] CommandParser(string str) + { + if (str == null || !(str.Length > 0)) return new string[0]; + int idx = str.Trim().IndexOf(" "); + if (idx == -1) return new string[] { str }; + int count = str.Length; + ArrayList list = new ArrayList(); + while (count > 0) + { + if (str[0] == '"') + { + int temp = str.IndexOf("\"", 1, str.Length - 1); + while (str[temp - 1] == '\\') + { + temp = str.IndexOf("\"", temp + 1, str.Length - temp - 1); + } + idx = temp + 1; + } + if (str[0] == '\'') + { + int temp = str.IndexOf("\'", 1, str.Length - 1); + while (str[temp - 1] == '\\') + { + temp = str.IndexOf("\'", temp + 1, str.Length - temp - 1); + } + idx = temp + 1; + } + string s = str.Substring(0, idx); + int left = count - idx; + str = str.Substring(idx, left).Trim(); + list.Add(s.Trim('"')); + count = str.Length; + idx = str.IndexOf(" "); + if (idx == -1) + { + string add = str.Trim('"', ' '); + if (add.Length > 0) + { + list.Add(add); + } + break; + } + } + return (string[])list.ToArray(typeof(string)); + } + + private static string ExtractFileDirectory(string path) + { + if (path == "/") + return path; + + var index = path.LastIndexOfAny("/".ToCharArray()); + return path.Substring(0, index); + } + + public static string AbsolutePath(string path) + { + if (path.Length == 0) + return path; + if (path.StartsWith("/") || path.StartsWith("\\")) return path; + if (path.Length == 1 && path == ".") return GlobalPath; + var basePath = ExtractFileDirectory(GlobalPath); + var relativePath = path; + while (relativePath.Substring(0, 2) == "..") + { + basePath = ExtractFileDirectory(basePath); + if (relativePath.Length < 3) + return ""; + else + relativePath = relativePath.Substring(3, relativePath.Length - 3); + if (relativePath.Length == 0) + return basePath; + } + return basePath + "/" + relativePath; + } + + public static void Log(string level, string message, params string[] args) + { + Console.WriteLine($"[{level}] {string.Format(message, args)}"); + } + } +} diff --git a/XLPakTool/Properties/AssemblyInfo.cs b/XLPakTool/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..f74eb82 --- /dev/null +++ b/XLPakTool/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// Общие сведения об этой сборке предоставляются следующим набором +// набора атрибутов. Измените значения этих атрибутов, чтобы изменить сведения, +// связанные со сборкой. +[assembly: AssemblyTitle("XLPakTool")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("XLPakTool")] +[assembly: AssemblyCopyright("Copyright © 2019")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Установка значения False для параметра ComVisible делает типы в этой сборке невидимыми +// для компонентов COM. Если необходимо обратиться к типу в этой сборке через +// COM, задайте атрибуту ComVisible значение TRUE для этого типа. +[assembly: ComVisible(false)] + +// Следующий GUID служит для идентификации библиотеки типов, если этот проект будет видимым для COM +[assembly: Guid("30356ff6-4a19-4b70-a776-d69d2167238a")] + +// Сведения о версии сборки состоят из следующих четырех значений: +// +// Основной номер версии +// Дополнительный номер версии +// Номер сборки +// Редакция +// +// Можно задать все значения или принять номер сборки и номер редакции по умолчанию. +// используя "*", как показано ниже: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/XLPakTool/XLPack.cs b/XLPakTool/XLPack.cs new file mode 100644 index 0000000..d80386a --- /dev/null +++ b/XLPakTool/XLPack.cs @@ -0,0 +1,105 @@ +using System; +using System.Runtime.InteropServices; + +namespace XLPakTool +{ + internal class XLPack + { + [DllImport("xlpack.dll", EntryPoint = "?ApplyPatchPak@@YA_NPBD0@Z", CallingConvention = CallingConvention.Cdecl)] + public static extern bool ApplyPatchPak([MarshalAs(UnmanagedType.LPStr)] string s1, [MarshalAs(UnmanagedType.LPStr)] string s2); + + [DllImport("xlpack.dll", EntryPoint = "?Copy@@YA_NPBD0@Z", CallingConvention = CallingConvention.Cdecl)] + public static extern bool Copy([MarshalAs(UnmanagedType.LPStr)] string from, [MarshalAs(UnmanagedType.LPStr)] string to); + + [DllImport("xlpack.dll", EntryPoint = "?CopyDir@@YA_NPBD0@Z", CallingConvention = CallingConvention.Cdecl)] + public static extern bool CopyDir([MarshalAs(UnmanagedType.LPStr)] string from, [MarshalAs(UnmanagedType.LPStr)] string to); + + [DllImport("xlpack.dll", EntryPoint = "?CreateFileSystem@@YA_NXZ", CallingConvention = CallingConvention.Cdecl)] + public static extern bool CreateFileSystem(); + + [DllImport("xlpack.dll", EntryPoint = "?DestroyFileLogHandler@@YAXPAX@Z", CallingConvention = CallingConvention.Cdecl)] + public static extern void DestroyFileLogHandler(IntPtr lp1); + + [DllImport("xlpack.dll", EntryPoint = "?DestroyFileSystem@@YAXXZ", CallingConvention = CallingConvention.Cdecl)] + public static extern void DestroyFileSystem(); + + [DllImport("xlpack.dll", EntryPoint = "?FDelete@@YA_NPBD@Z", CallingConvention = CallingConvention.Cdecl)] + public static extern bool FDelete([MarshalAs(UnmanagedType.LPStr)] string where); + + [DllImport("xlpack.dll", EntryPoint = "?FindClose@@YAHH@Z", CallingConvention = CallingConvention.Cdecl)] + public static extern int FindClose(int i); + + [DllImport("xlpack.dll", EntryPoint = "?FindFirst@@YAHPBDPAUafs_finddata@@@Z", CallingConvention = CallingConvention.Cdecl)] + public static extern int FindFirst([MarshalAs(UnmanagedType.LPStr)] string file, ref afs_finddata fd); + + [DllImport("xlpack.dll", EntryPoint = "?FindNext@@YAHHPAUafs_finddata@@@Z", CallingConvention = CallingConvention.Cdecl)] + public static extern int FindNext(int i, ref afs_finddata fd); + + [DllImport("xlpack.dll", EntryPoint = "?GetFileName@@YAPBDPBUafs_finddata@@@Z", CallingConvention = CallingConvention.Cdecl)] + public static extern IntPtr GetFileName(ref afs_finddata fd); + + [DllImport("xlpack.dll", EntryPoint = "?IsDirectory@@YA_NPBUafs_finddata@@@Z", CallingConvention = CallingConvention.Cdecl)] + public static extern char IsDirectory(ref afs_finddata fd); + + [DllImport("xlpack.dll", EntryPoint = "?IsFileExist@@YA_NPBD@Z", CallingConvention = CallingConvention.Cdecl)] + public static extern bool IsFileExist(char[] file); + + [DllImport("xlpack.dll", EntryPoint = "?Mount@@YAPAXPBD0_N@Z", CallingConvention = CallingConvention.Cdecl)] + public static extern IntPtr Mount([MarshalAs(UnmanagedType.LPStr)] string where, [MarshalAs(UnmanagedType.LPStr)] string which, [MarshalAs(UnmanagedType.Bool)] bool editable); + + [DllImport("xlpack.dll", EntryPoint = "?SetFileLogHandler@@YAPAXPBDP6AX0ZZ@Z", CallingConvention = CallingConvention.Cdecl)] + public static extern IntPtr SetFileLogHandler([MarshalAs(UnmanagedType.LPStr)] string s, XlFunc f); + + [DllImport("xlpack.dll", EntryPoint = "?Unmount@@YA_NPBD@Z", CallingConvention = CallingConvention.Cdecl)] + public static extern bool Unmount([MarshalAs(UnmanagedType.LPStr)] string where); + + [DllImport("xlpack.dll", EntryPoint = "?DeleteDir@@YA_NPBD@Z", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] + public static extern bool DeleteDir([MarshalAs(UnmanagedType.LPStr)] string path); + + [StructLayout(LayoutKind.Explicit)] + public struct afs_finddata + { + [FieldOffset(0)] public long data0; + [FieldOffset(8)] public long data1; + [FieldOffset(16)] public long data2; + [FieldOffset(24)] public long data3; + [FieldOffset(32)] public long data4; + [FieldOffset(40)] public long data5; + [FieldOffset(48)] public long data6; + [FieldOffset(56)] public long data7; + [FieldOffset(64)] public long data8; + [FieldOffset(72)] public long data9; + [FieldOffset(80)] public long data10; + } + + [StructLayout(LayoutKind.Explicit)] + public struct File + { + [FieldOffset(0)] public uint pntr; + [FieldOffset(4)] public uint cnt; + [FieldOffset(8)] public uint based; // base + [FieldOffset(12)] public uint flag; + [FieldOffset(16)] public uint file; + [FieldOffset(20)] public uint charbuf; + [FieldOffset(24)] public uint bufsize; + [FieldOffset(28)] public uint tmpfname; + } + + [StructLayout(LayoutKind.Explicit)] + public struct XlFileInfo + { + [FieldOffset(0)] public uint dwFileAttributes; + [FieldOffset(4)] public ulong ftCreationTime; + [FieldOffset(12)] public ulong ftLastAccessTime; + [FieldOffset(20)] public ulong ftLastWriteTime; + [FieldOffset(28)] public uint dwVolumeSerialNumber; + [FieldOffset(32)] public uint nFileSizeHigh; + [FieldOffset(36)] public uint nFileSizeLow; + [FieldOffset(40)] public uint nNumberOfLinks; + [FieldOffset(44)] public uint nFileIndexHigh; + [FieldOffset(48)] public uint nFileIndexLow; + } + + public delegate void XlFunc(params string[] p); + } +} diff --git a/XLPakTool/XLPakTool.csproj b/XLPakTool/XLPakTool.csproj new file mode 100644 index 0000000..b49d83b --- /dev/null +++ b/XLPakTool/XLPakTool.csproj @@ -0,0 +1,59 @@ + + + + + Debug + AnyCPU + {30356FF6-4A19-4B70-A776-D69D2167238A} + Exe + XLPakTool + XLPakTool + v4.6.1 + 512 + true + true + + + AnyCPU + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + AnyCPU + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + + + + + + + + + + + + + + + + + + + Always + + + + \ No newline at end of file