Last active
November 22, 2020 04:04
-
-
Save paralleltree/a0e4b970e9fc5bfbf496a71ea654dd85 to your computer and use it in GitHub Desktop.
emmファイルを参照してリソースをバックアップするPowerShellスクリプトを生成するやつ
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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