Skip to content

Instantly share code, notes, and snippets.

@paralleltree
Last active November 22, 2020 04:04
Show Gist options
  • Save paralleltree/a0e4b970e9fc5bfbf496a71ea654dd85 to your computer and use it in GitHub Desktop.
Save paralleltree/a0e4b970e9fc5bfbf496a71ea654dd85 to your computer and use it in GitHub Desktop.
emmファイルを参照してリソースをバックアップするPowerShellスクリプトを生成するやつ
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
namespace EmmBackupGen
{
class Program
{
static void Main(string[] args)
{
var baseDir = @"D:\b\MMD\MikuMikuDance_v926x64\UserFile";
var dirPattern = new Regex(@"(?<=UserFile\\)([^\r\n\\]*\\){2}", RegexOptions.Multiline);
var emms = Directory.EnumerateFiles(baseDir, "*.emm");
var dirs = emms
.SelectMany(p => dirPattern.Matches(File.ReadAllText(p, encoding)).Cast<Match>())
.GroupBy(p => p.Value)
.Select(p => p.Key)
.ToList();
var refDirs = dirs
.SelectMany(p =>
{
try
{
return Directory.EnumerateFiles(Path.Combine(baseDir, p))
.Where(q => File.GetAttributes(q).HasFlag(FileAttributes.ReparsePoint))
.Select(q => NativeMethods.GetFinalPath(q));
}
catch (DirectoryNotFoundException)
{
Console.WriteLine($"warn: no such directory: {p}");
return Enumerable.Empty<string>();
}
})
.Select(p => dirPattern.Match(p))
.Where(p => p.Success)
.GroupBy(p => p.Value)
.Select(p => p.Key)
.ToList();
var union = dirs.Union(refDirs).ToList();
Console.WriteLine($"dirs: {dirs.Count}");
Console.WriteLine($"referencedDirs: {refDirs.Count}");
Console.WriteLine($"totalDirs: {union.Count}");
using (var writer = new StreamWriter(@"D:\b\MMD\minibk.ps1", false, Encoding.UTF8))
{
writer.WriteLine(@"$ErrorActionPreference = ""Stop""");
string basePath = baseDir.Substring(Path.GetPathRoot(baseDir).Length);
foreach (var dir in union)
{
string path = Path.Combine(basePath, dir);
writer.WriteLine(CopyCommand(Path.Combine(@"D:\", path), Path.Combine(@"I:\", path)));
}
}
}
static readonly Encoding encoding = Encoding.GetEncoding("shift-jis");
static string CopyCommand(string src, string dest) => $"Invoke-Expression 'xcopy /B /D /H /S /E /K /Y \"{EscapePath(src.TrimEnd(Path.DirectorySeparatorChar))}\" \"{EscapePath(dest)}\"'";
static string EscapePath(string path) => path.Replace("'", "''");
}
// ref: https://stackoverflow.com/questions/2302416/how-to-obtain-the-target-of-a-symbolic-link-or-reparse-point-using-net
static class NativeMethods
{
private static readonly IntPtr INVALID_HANDLE_VALUE = new IntPtr(-1);
private const uint FILE_READ_EA = 0x0008;
private const uint FILE_FLAG_BACKUP_SEMANTICS = 0x2000000;
[DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Auto)]
static extern uint GetFinalPathNameByHandle(
IntPtr hFile,
[MarshalAs(UnmanagedType.LPTStr)] StringBuilder lpszFilePath,
uint cchFilePath,
uint dwFlags
);
[DllImport("kernel32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
static extern bool CloseHandle(IntPtr hObject);
[DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Auto)]
public static extern IntPtr CreateFile(
[MarshalAs(UnmanagedType.LPTStr)] string filename,
[MarshalAs(UnmanagedType.U4)] uint access,
[MarshalAs(UnmanagedType.U4)] FileShare share,
IntPtr securityAttributes,
[MarshalAs(UnmanagedType.U4)] FileMode creationDisposition,
[MarshalAs(UnmanagedType.U4)] uint flagsAndAttributes,
IntPtr templateFile
);
public static string GetFinalPath(string path)
{
var handle = CreateFile(path, FILE_READ_EA, FileShare.Read, IntPtr.Zero, FileMode.Open, FILE_FLAG_BACKUP_SEMANTICS, IntPtr.Zero);
if (handle == INVALID_HANDLE_VALUE) throw new System.ComponentModel.Win32Exception();
try
{
var sb = new StringBuilder(1024);
if (GetFinalPathNameByHandle(handle, sb, 1024, 0) == 0)
throw new System.ComponentModel.Win32Exception();
return sb.ToString();
}
finally
{
CloseHandle(handle);
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment