// Entry for MC public static int MC_Main(TextWriter stdout, TextWriter stderr, String args) { mcInteropMode = true; Console.SetOut(stdout); Console.SetError(stderr); CommandLineArguments cmdargs = new CommandLineArguments(args); return InnerMain(cmdargs); }
// Actual inner that executes commands static int InnerMain(CommandLineArguments cmdargs) { Console.ForegroundColor = ConsoleColor.Cyan; Console.WriteLine(Resources.CopyrightNotice); Console.ForegroundColor = ConsoleColor.Gray; if (cmdargs.Count == 0) { Console.WriteLine(Resources.NoArgsMessage); Console.WriteLine("\nPress any key to continue..."); Console.ReadKey(); Console.Clear(); Console.ForegroundColor = ConsoleColor.Cyan; Console.WriteLine(Resources.CopyrightNotice); Console.ForegroundColor = ConsoleColor.Gray; while (true) { Console.ForegroundColor = ConsoleColor.Yellow; Console.Write("MCU> "); Console.ForegroundColor = ConsoleColor.Gray; CommandLineArguments args = new CommandLineArguments("MCU " + Console.ReadLine()); if (args.Count == 0) continue; Int32 code = MCU_Run(args); Console.ForegroundColor = ConsoleColor.Yellow; Console.WriteLine("Command finished and returned code {0}\n", code); Console.ForegroundColor = ConsoleColor.Gray; } } else { return MCU_Run(cmdargs); } }
static int Pack(CommandLineArguments cmdargs) { // Handle no-arg case if (cmdargs.Count == 1) { WriteMessage(MessageType.Error, "Error 904: No arguments detected\n"); return 904; } // Handle help request if (cmdargs[1].Type == CommandLineArgument.ArgumentType.Option && cmdargs[1].Name == "?") { Console.WriteLine(Resources.PackHelp); return mcInteropMode ? 903 : 0; } // Define option variables Boolean saveState = false; Boolean trimArchive = false; Boolean lockArchive = false; Boolean verbose = false; Boolean patch = false; String input = null; String output = null; // Get options and arguments for (int i = 1; i < cmdargs.Count; i++) { if (cmdargs[i].Type == CommandLineArgument.ArgumentType.Argument) { if (input == null) { input = cmdargs[i].Name; output = Path.GetDirectoryName(input) + ".mcpak"; } else if (input != null) { output = cmdargs[i].Name; } } else { switch (cmdargs[i].Name.ToLower()) { case "savestate": saveState = true; break; case "trim": saveState = true; trimArchive = true; break; case "lock": lockArchive = true; break; case "patch": patch = true; break; case "verbose": verbose = true; break; } } } // Validate if (input == null) { WriteMessage(MessageType.Error, "Error 907: Input path not specified!\n"); return 907; } if (!File.Exists(input)) { WriteMessage(MessageType.Error, "Error 909: Specified input file does not exist!\n"); return 909; } // Pack Resources //#if !DEBUG try { //#endif // Create Resource Manager CharacterResourceManager resmgr = null; // Load resource manager according to the input file supplied if (Path.GetExtension(input) == ".mcres") { resmgr = CharacterResourceManager.FromXML(input); resmgr.FileSystemProxy = new DefaultFileSystemProxy(Path.GetDirectoryName(input)); } else if (Path.GetExtension(input) == ".mcpak") { AFSFileSystemProxy proxy = new AFSFileSystemProxy(input); PackageEntry mcresEntry = proxy.Archive.TryGetEntry("character.mcres"); if (mcresEntry == null) { WriteMessage(MessageType.Error, "Error 911: Cannot find resource descriptor entry!\n"); return 911; } using (ExStream exs = new ExStream()) { proxy.Archive.Extract(mcresEntry, exs); exs.Position = 0; resmgr = CharacterResourceManager.FromXML(exs); resmgr.FileSystemProxy = proxy; } } else if (Path.GetExtension(input) == ".ini") { WriteMessage(MessageType.Error, "Error 912: Directly packing Legacy directories is not supported!\n" + " You may try using LEGACY command on the input file (directory) first.\n" + " For more information type \"mcu LEGACY /?\"\n"); return 912; } else { WriteMessage(MessageType.Error, "Error 913: Input file extension [{0}] not recognized!\n", Path.GetExtension(input)); return 913; } // Load state if needed if (saveState) { try { Stream stateStream = resmgr.FileSystemProxy.GetSavedStateStream(); if (stateStream != null) LoadCharacterFromStream(resmgr, stateStream); else { if (!trimArchive) { WriteMessage(MessageType.Routine, "No saved state found! No state will be saved into the archive!\n"); saveState = false; } else { WriteMessage(MessageType.Error, "Error 914: No saved state found! Cannot apply /trim option!\n"); return 914; } } } catch (Exception ex) { if (!trimArchive) { WriteMessage(MessageType.Routine, "No saved state found! No state will be saved into the archive!\n"); saveState = false; } else { WriteMessage(MessageType.Error, "Error 914: No saved state found! Cannot apply /trim option!\n"); return 914; } throw ex; } } // Create packager CharacterResourcePacker packer = new CharacterResourcePacker(output, resmgr); packer.SaveState = saveState; packer.LockCharacter = lockArchive; packer.OmitUnusedResources = trimArchive; packer.IsPatch = patch; // Set up verbose mode if needed if (verbose) { packer.ArchiveGenerator.NotifyDetails = (String s) => { Console.Write(" {0}", s); }; } // Start making archive if (verbose) WriteMessage(MessageType.Routine, ""); packer.PackResources(); if (verbose) Console.WriteLine(); // Report success if (!packer.ArchiveGenerator.HasErrors) { WriteMessage(MessageType.Routine, "Success!\n"); return 0; } else { WriteMessage(MessageType.Routine, "Error 918: Archive generator reported error(s)!\n"); return 918; } //#if !DEBUG } catch (Exception ex) { WriteMessage(MessageType.Error, "An exception of type {0} occured!\n" + " Exception Message: {1}\n" + " Stack Trace:\n{2}", ex.GetType().Name, ex.Message, ex.StackTrace); return 999; } //#endif }
static int Cleanup(CommandLineArguments cmdargs) { // Handle no-arg case if (cmdargs.Count == 1) { WriteMessage(MessageType.Error, "Error 904: No arguments detected\n"); return 904; } // Handle help request if (cmdargs[1].Type == CommandLineArgument.ArgumentType.Option && cmdargs[1].Name == "?") { Console.WriteLine(Resources.CleanupHelp); return mcInteropMode ? 903 : 0; } String input = null; String moveLocation = "mcu.unused"; Boolean delete = false; Boolean noconfirm = false; Boolean deepClean = false; String[] ignore = new String[] { "mcres", "mcs", "ini" }; for (int i = 1; i < cmdargs.Count; i++) { if (cmdargs[i].Type == CommandLineArgument.ArgumentType.Argument) input = cmdargs[i].Name; else { switch (cmdargs[i].Name.ToLower()) { case "move": { if (cmdargs[i].Arguments.Length == 0) { WriteMessage(MessageType.Error, "Error 905: /move must have a string argument!"); return 905; } moveLocation = cmdargs[i].Arguments[0]; } break; case "delete": delete = true; break; case "noconfirm": noconfirm = true; break; case "deepclean": deepClean = true; break; case "ignore": { String[] tmpIgnore = new String[ignore.Length + cmdargs[i].Arguments.Length]; Array.Copy(ignore, tmpIgnore, ignore.Length); Array.Copy(cmdargs[i].Arguments, 0, tmpIgnore, ignore.Length, cmdargs[i].Arguments.Length); //ignore = cmdargs[i].Arguments; ignore = tmpIgnore; } break; } } } // validate if (input == null) { WriteMessage(MessageType.Error, "Error 907: Input Path not specified!\n"); return 907; } if (!File.Exists(input)) { WriteMessage(MessageType.Error, "Error 909: Input file does not exist!\n"); return 909; } if (Path.GetExtension(input).ToLower() != ".mcres") { WriteMessage(MessageType.Error, "Error 913: Input file not supported!\n"); return 913; } // Load Resource manager CharacterResourceManager resmgr = CharacterResourceManager.FromXML(input); // Get All Files String root = Path.GetDirectoryName(input); Dictionary<String, Byte> files = new Dictionary<string, byte>(StringComparer.CurrentCultureIgnoreCase); foreach (CharaSetGroup csg in resmgr) foreach (CharaSet cset in csg) foreach (CharaPart cpart in cset) { if (!files.ContainsKey(cpart.FileName)) files.Add(cpart.FileName, 0); } // Recursive Lambda =w= List<String> unused = new List<string>(); Dictionary<String, String> hash2name = new Dictionary<string, string>(StringComparer.CurrentCultureIgnoreCase); Dictionary<String, String> relink = new Dictionary<string, string>(StringComparer.CurrentCultureIgnoreCase); SHA256 hasher = SHA256.Create(); Action<String> processDir = null; processDir = (String path) => { if (File.Exists(path)) { if (Path.GetExtension(path).Length > 1 && ignore.Contains(Path.GetExtension(path).Substring(1))) return; if (!files.ContainsKey(libWyvernzora.Path.GetRelativePath(path, root))) { unused.Add(libWyvernzora.Path.GetRelativePath(path, root)); } else if (deepClean) { String hash = null; using (FileStream fs = new FileStream(path, FileMode.Open)) hash = libWyvernzora.HexTools.Byte2String(hasher.ComputeHash(fs)).ToUpper(); String relPath = libWyvernzora.Path.GetRelativePath(path, root); if (!hash2name.ContainsKey(hash)) hash2name.Add(hash, relPath); else { String identicalFile = hash2name[hash]; relink.Add(relPath, identicalFile); unused.Add(relPath); } } } else if (Directory.Exists(path)) { if (Path.GetDirectoryName(path).ToLower().StartsWith("mcu.")) return; if (StringComparer.CurrentCultureIgnoreCase.Equals(Path.Combine(root, moveLocation), path)) return; if (Path.IsPathRooted(moveLocation) && StringComparer.CurrentCultureIgnoreCase.Equals(moveLocation, path)) return; foreach (String dir in Directory.GetDirectories(path)) processDir(dir); foreach (String file in Directory.GetFiles(path)) processDir(file); } }; processDir(root); if (unused.Count != 0) { WriteMessage(MessageType.Routine, "Following files have been detected as unused:\n"); foreach (String path in unused) { Console.WriteLine(" {0}", path); } Console.WriteLine(); if (!noconfirm) { WriteMessage(MessageType.Routine, "These files will now be {0}, are you sure to proceed? [Y/N]", delete ? "deleted" : "moved"); while (true) { String response = Console.ReadLine(); if (response.ToUpper().StartsWith("Y")) break; else if (response.ToUpper().StartsWith("N")) { WriteMessage(MessageType.Routine, "Action cancelled by user.\n"); return 915; } } } if (deepClean) { foreach (CharaSetGroup csg in resmgr) foreach (CharaSet cset in csg) foreach (CharaPart cpart in cset) { if (relink.ContainsKey(cpart.FileName)) cpart.FileName = relink[cpart.FileName]; } using (FileStream fs = new FileStream(input, FileMode.Create)) { XmlWriter xw = XmlTextWriter.Create(fs, new XmlWriterSettings() { Encoding = Encoding.UTF8, Indent = true, IndentChars = "\t" }); resmgr.ToXml(xw); xw.Flush(); } } if (!Path.IsPathRooted(moveLocation)) moveLocation = Path.Combine(root, moveLocation); foreach (String path in unused) { String fname = Path.Combine(root, path); if (delete) { File.Delete(fname); } else { String newPath = Path.Combine(moveLocation, path); if (File.Exists(newPath)) File.Delete(newPath); if (!Directory.Exists(Path.GetDirectoryName(newPath))) Directory.CreateDirectory(Path.GetDirectoryName(newPath)); File.Move(fname, newPath); } } WriteMessage(MessageType.Routine, "Success!\n"); return 0; } else { WriteMessage(MessageType.Routine, "Resource set is already clean, nothing needs to be done.\n"); return 0; } }
static int Legacy(CommandLineArguments cmdargs) { // Handle no-arg case if (cmdargs.Count == 1) { WriteMessage(MessageType.Error, "Error 904: No arguments detected\n"); return 904; } // Handle help request if (cmdargs[1].Type == CommandLineArgument.ArgumentType.Option && cmdargs[1].Name == "?") { Console.WriteLine(Resources.LegacyHelp); return mcInteropMode ? 903 : 0; // MC Interop Mode does not support /? flag and treats it as an error } // Define arg variables Boolean ini = true; Int32 width = -1; Int32 height = -1; String charname = null; String dir = null; #region Arg Validation // Check all args for (int i = 1; i < cmdargs.Count; i++) { if (cmdargs[i].Type == CommandLineArgument.ArgumentType.Argument) dir = cmdargs[i].Name; else { switch (cmdargs[i].Name.ToLower()) { case "ini": { Boolean tmp; if (cmdargs[i].Arguments.Length == 0 || !Boolean.TryParse(cmdargs[i].Arguments[0], out tmp)) { WriteMessage(MessageType.Error, "Error 905: /ini must have a boolean argument!\n"); return 905; } ini = tmp; } break; case "width": { Int32 tmp; if (cmdargs[i].Arguments.Length == 0 || !Int32.TryParse(cmdargs[i].Arguments[0], out tmp)) { WriteMessage(MessageType.Error, "Error 905: /width must have an integer argument!\n"); return 905; } width = tmp; } break; case "height": { Int32 tmp; if (cmdargs[i].Arguments.Length == 0 || !Int32.TryParse(cmdargs[i].Arguments[0], out tmp)) { WriteMessage(MessageType.Error, "Error 905: /height must have an integer argument!\n"); return 905; } height = tmp; } break; case "charname": { if (cmdargs[i].Arguments.Length == 0) { WriteMessage(MessageType.Error, "Error 905: /charaname must have a string argument!\n"); return 905; } charname = cmdargs[i].Arguments[0]; } break; } } } #endregion // Validate Input if (ini && Path.GetFileName(dir) == "character.ini") { dir = Path.GetDirectoryName(dir); } if (!ini && (height < 0 || width < 0)) { WriteMessage(MessageType.Error, "Error 906: When /ini:false, /height and /width must be specified\n"); return 906; } if (dir == null) { WriteMessage(MessageType.Error, "Error 906: Directory to load was not specified!\n"); return 907; } if (!Directory.Exists(dir) || (ini && !File.Exists(Path.Combine(dir, "character.ini")))) { WriteMessage(MessageType.Error, "Error 906: Specified directory (or character.ini) does not exist!\n"); return 909; } // Start working on the task CharaxDirHelper dirHelper = null; if (ini) dirHelper = new CharaxDirHelper(Path.Combine(dir, "character.ini")); else dirHelper = new CharaxDirHelper(dir, width, height); // Set charname if it was specified if (charname != null) dirHelper.CharaName = charname; // Write the descriptor dirHelper.GenerateDescriptor(Path.Combine(dir, "character.mcres")); // Report success WriteMessage(MessageType.Routine, "Success!\n"); return 0; }
static int Help(CommandLineArguments cmdargs) { Console.WriteLine(Resources.Help); return mcInteropMode ? 903 : 0; }
static int MCU_Run(CommandLineArguments cmdargs) { if (cmdargs[0].Type == CommandLineArgument.ArgumentType.Option) { WriteMessage(MessageType.Error, "Error 901: Command cannot be a command line option\n"); return 901; } try { String command = cmdargs[0].Name.ToUpper(); switch (command) { case "HELP": return Help(cmdargs); case "LEGACY": return Legacy(cmdargs); case "PACK": return Pack(cmdargs); case "CLEANUP": return Cleanup(cmdargs); case "VERIFY": return Verify(cmdargs); case "DEPLOY": return Deploy(cmdargs); default: { WriteMessage(MessageType.Error, "Error 902: Command not recognized!\n"); return 902; } } } catch (Exception ex) { Console.Error.WriteLine("An exception of type {0} occured!", ex.GetType().Name); Console.Error.WriteLine("Exception Message: {0}", ex.Message); Console.Error.WriteLine("========== Stack Trace ==========\n{0}", ex.StackTrace); return 999; } }
static int Deploy(CommandLineArguments cmdargs) { // Handle no-arg case if (cmdargs.Count == 1) { WriteMessage(MessageType.Error, "Error 904: No arguments detected\n"); return 904; } // Handle help request if (cmdargs[1].Type == CommandLineArgument.ArgumentType.Option && cmdargs[1].Name == "?") { Console.WriteLine(Resources.DeployHelp); return mcInteropMode ? 903 : 0; } // Get Options String deployRoot = "patches\\%PATCHNAME%"; String targetPath = null; String patchPath = null; Boolean merge = false; Boolean update = false; Boolean checkhash = false; CollisionHandling collisionMode = CollisionHandling.Prompt; #region Get Options for (int i = 1; i < cmdargs.Count; i++) { if (cmdargs[i].Type == CommandLineArgument.ArgumentType.Argument) { if (patchPath == null) patchPath = cmdargs[i].Name; else if (targetPath == null) targetPath = cmdargs[i].Name; } else { switch (cmdargs[i].Name.ToLower()) { case "deployroot": { if (cmdargs[i].Arguments.Length == 0) { WriteMessage(MessageType.Error, "Error 905: /deployroot must have a string argument!\n"); return 905; } deployRoot = cmdargs[i].Arguments[0]; } break; case "merge": { merge = true; } break; case "update": { update = true; } break; case "checkhash": checkhash = true; break; case "collision": { if (cmdargs[i].Arguments.Length == 0) { WriteMessage(MessageType.Error, "Error 905: /collision must have a string argument!\n"); return 905; } switch (cmdargs[i].Arguments[0].ToLower()) { case "prompt": collisionMode = CollisionHandling.Prompt; break; case "error": collisionMode = CollisionHandling.Error; break; case "overwrite": collisionMode = CollisionHandling.Overwrite; break; case "rename": collisionMode = CollisionHandling.Rename; break; case "skip": collisionMode = CollisionHandling.Skip; break; default: { WriteMessage(MessageType.Error, "Error 905: /collision argument \"{0}\" not recognized!\n", cmdargs[i].Arguments[0]); return 905; } } } break; } } } #endregion // Verify if (patchPath == null) { WriteMessage(MessageType.Error, "Error 907: Pacth file not specified!\n"); return 907; } if (targetPath == null) { WriteMessage(MessageType.Error, "Error 908: Target path not specified!\n"); return 908; } if (!File.Exists(patchPath)) { WriteMessage(MessageType.Error, "Error 909: Specified patch file does not exist!\n"); return 909; } if (!File.Exists(targetPath)) { WriteMessage(MessageType.Error, "Error 910: Specified target resource descriptor does not exist!\n"); return 910; } if (Path.GetExtension(targetPath).ToLower() != ".mcres") { WriteMessage(MessageType.Error, "Error 916: Target file not supported!\n"); return 916; } if (Path.GetExtension(patchPath).ToLower() != ".mcpak") { WriteMessage(MessageType.Error, "Error 913: Patch file not supported!\n"); return 913; } // Change settings if (merge) deployRoot = ""; if (update) collisionMode = CollisionHandling.Overwrite; // load both resources // load target resource manager CharacterResourceManager target = CharacterResourceManager.FromXML(targetPath); String targetRoot = Path.GetDirectoryName(targetPath); target.FileSystemProxy = new DefaultFileSystemProxy(targetRoot); // load patch resource manager CharacterResourceManager patch = null; AFSFileSystemProxy afsproxy = new AFSFileSystemProxy(patchPath); using (ExStream descrs = new ExStream()) { PackageEntry mcres = afsproxy.Archive.TryGetEntry("character.mcres"); if (mcres == null) { WriteMessage(MessageType.Error, "Error 911: Cannot find descriptor in patch archive!\n"); return 911; } afsproxy.Archive.Extract(mcres, descrs); descrs.Position = 0; patch = CharacterResourceManager.FromXML(descrs); } // load patch filename mapping if it exists Dictionary<String, String> fileNameMap = null; PackageEntry fmapentry = afsproxy.Archive.TryGetEntry(".patch"); if (fmapentry != null) { fileNameMap = new Dictionary<string, string>(); XmlDocument fmapdoc = new XmlDocument(); using (ExStream fmaps = new ExStream()) { afsproxy.Archive.Extract(fmapentry, fmaps); fmaps.Position = 0; fmapdoc.Load(fmaps); } foreach (XmlNode mapNode in fmapdoc.SelectNodes("FileNameMap/Mapping")) { String hashed = mapNode.Attributes["key"].InnerText; String original = mapNode.InnerText; fileNameMap.Add(hashed, original); } } patch.FileSystemProxy = afsproxy; // finalize deploy root deployRoot = deployRoot.Replace("%PATCHNAME%", Path.GetFileNameWithoutExtension(patchPath)); // Virtual Extraction to identify possible problems Dictionary<PackageEntry, String> extractionPaths = new Dictionary<PackageEntry, string>(); Dictionary<String, String> relink = new Dictionary<string, string>(StringComparer.CurrentCultureIgnoreCase); List<PackageEntry> problems = new List<PackageEntry>(); foreach (PackageEntry entry in afsproxy.Archive.Entries) { if (entry.Name.ToLower() == ".patch" || entry.Name.ToLower() == ".lock") continue; String ext = Path.GetExtension(entry.Name).ToLower(); if (ext == ".mcres" || ext == ".mcs") continue; String entryName = (fileNameMap != null && fileNameMap.ContainsKey(entry.Name)) ? fileNameMap[entry.Name] : entry.Name; String relExtrPath = Path.Combine(deployRoot, entryName); String absExtrPath = Path.Combine(targetRoot, relExtrPath); if (File.Exists(absExtrPath)) { if (checkhash) { // Files collided, verify whether thay are identical String targetHash = GetSHA(absExtrPath); String patchHash = null; using (ExStream exs = new ExStream()) { afsproxy.Archive.Extract(entry, exs); patchHash = GetSHA(exs); } if (targetHash == patchHash) continue; // Both files are identical, skip extraction } // they are not identical or checkhash disabled... need to do something CollisionHandling handling = collisionMode; if (collisionMode == CollisionHandling.Prompt) { WriteMessage(MessageType.Routine, "File collision detected:\n"); Console.WriteLine(" Target: {0}", relExtrPath); Console.WriteLine(" Patch: {0}", entryName); Console.WriteLine(" 1) Error\n 2) Rename\n 3) Overwrite\n 4) Skip"); while (true) { Console.Write(" Action:"); Int32 result; if (!Int32.TryParse(Console.ReadLine(), out result)) continue; if (result < 1 || result > 4) continue; handling = (CollisionHandling)result; if (handling == CollisionHandling.Error) // if user choses error, no need to prompt for further collisions collisionMode = CollisionHandling.Error; break; } } // handle collision switch (handling) { case CollisionHandling.Error: { problems.Add(entry); continue; } case CollisionHandling.Rename: { Int32 cid = 1; string nfname = relExtrPath; while (true) { nfname = Path.Combine(Path.GetDirectoryName(relExtrPath), String.Format("{0}({1}){2}", Path.GetFileNameWithoutExtension(relExtrPath), cid++, Path.GetExtension(relExtrPath))); if (!File.Exists(Path.Combine(targetRoot, nfname))) break; } extractionPaths.Add(entry, nfname); relink.Add(entry.Name, nfname); continue; } case CollisionHandling.Overwrite: extractionPaths.Add(entry, relExtrPath); relink.Add(entry.Name, relExtrPath); continue; case CollisionHandling.Skip: relink.Add(entry.Name, relExtrPath); continue; } } else { extractionPaths.Add(entry, relExtrPath); relink.Add(entry.Name, relExtrPath); } } if (problems.Count != 0) { WriteMessage(MessageType.Error, "{0} Error(s) have been detected!\n", problems.Count); foreach (PackageEntry entry in problems) { String entryName = (fileNameMap != null && fileNameMap.ContainsKey(entry.Name)) ? fileNameMap[entry.Name] : entry.Name; String relExtrPath = Path.Combine(deployRoot, entryName); Console.WriteLine(" Collision with {0}", relExtrPath); } WriteMessage(MessageType.Error, "Error 917: Deployment failed due to unresolved file collisions!\n"); return 917; } // Extract Files foreach (KeyValuePair<PackageEntry, String> extr in extractionPaths) { String path = Path.Combine(targetRoot, extr.Value); afsproxy.Archive.ExtractSingle(extr.Key, path); } // Merge Color Presets foreach (String colorg in patch.Presets.Keys) { if (!target.Presets.ContainsKey(colorg)) target.Presets.Add(colorg, patch.Presets[colorg]); } // Merge Character Part Records foreach (CharaSetGroup patchsg in patch) { // Skip empty set groups if (patchsg.Count == 0) continue; // Get target chara set group CharaSetGroup targetsg = target.GetGroupByName(patchsg.Name); if (targetsg == null) { // if it doesn't exist, create it targetsg = new CharaSetGroup(patchsg.Name, patchsg.Multiselect); target.Add(targetsg); } // foreach chara set in the patch group foreach (CharaSet patchset in patchsg) { // Get target chara set CharaSet targetset = targetsg.GetSetByName(patchset.Name); // if the set exists and update flag is up, delete it if (targetset != null && update) { targetsg.Remove(targetset); targetset = new CharaSet(patchset.Name); } else if (targetset != null) { // if the set exists but update flag is not up, rename the new set Int32 index = 1; Match m = Regex.Match(patchset.Name, "(?<name>.+?)(?<index>[0-9]+)"); if (m.Success) { index = Int32.Parse(m.Result("${index}")); patchset.Name = m.Result("${name}"); } while (true) { if (targetsg.GetSetByName(patchset.Name + (index++).ToString()) == null) break; } targetset = new CharaSet(patchset.Name + (index).ToString()); } else { // if it doesn't exist, create it targetset = new CharaSet(patchset.Name); } // Add target set to target targetsg.Add(targetset); // copy all parts taking file renaming into consideration foreach (CharaPart patchpart in patchset) { String filePath = relink[patchpart.FileName]; targetset.Add(new CharaPart(patchpart.UnadjustedLayer, filePath, patchpart.ColorScheme)); } } targetsg.SortSets(); } if (File.Exists(Path.ChangeExtension(targetPath, "bak"))) File.Delete(Path.ChangeExtension(targetPath, "bak")); File.Move(targetPath, Path.ChangeExtension(targetPath, "bak")); using (XmlWriter xw = XmlTextWriter.Create(targetPath, new XmlWriterSettings() { Indent = true, IndentChars = "\t", Encoding = Encoding.UTF8 })) { target.ToXml(xw); xw.Flush(); } WriteMessage(MessageType.Routine, "Success!\n"); return 0; }
static int Verify(CommandLineArguments cmdargs) { // Handle no-arg case if (cmdargs.Count == 1) { WriteMessage(MessageType.Error, "Error 904: No arguments detected\n"); return 904; } // Handle help request if (cmdargs[1].Type == CommandLineArgument.ArgumentType.Option && cmdargs[1].Name == "?") { Console.WriteLine(Resources.VerifyHelp); return mcInteropMode ? 903 : 0; } // Get options String input = null; Boolean delMissing = false; Boolean noconfirm = false; for (int i = 1; i < cmdargs.Count; i++) { if (cmdargs[i].Type == CommandLineArgument.ArgumentType.Argument) input = cmdargs[i].Name; else { switch (cmdargs[i].Name.ToLower()) { case "deletemissing": delMissing = true; break; case "noconfirm": noconfirm = true; break; } } } // validate if (input == null) { WriteMessage(MessageType.Error, "Error 907: Input Path not specified!\n"); return 907; } if (!File.Exists(input)) { WriteMessage(MessageType.Error, "Error 909: Input file does not exist!\n"); return 909; } if (Path.GetExtension(input).ToLower() != ".mcres") { WriteMessage(MessageType.Error, "Error 913: Input file not supported!\n"); return 913; } // Load Resource manager CharacterResourceManager resmgr = CharacterResourceManager.FromXML(input); String root = Path.GetDirectoryName(input); // Verify List<CharaSet> missing = new List<CharaSet>(); foreach (CharaSetGroup csg in resmgr) foreach (CharaSet cset in csg) foreach (CharaPart cpart in cset) { if (!File.Exists(Path.Combine(root, cpart.FileName))) { missing.Add(cset); break; } } if (missing.Count != 0) { // print out missing list WriteMessage(MessageType.Routine, "Mising files have been detected in following sets:\n"); foreach (CharaSet cset in missing) { Console.WriteLine(" {0}/{1}", cset.Parent.Name, cset.Name); } // confirmation if (delMissing && !noconfirm) { WriteMessage(MessageType.Routine, "These sets will now be deleted, are you sure to proceed? [Y/N]"); while (true) { String response = Console.ReadLine(); if (response.ToUpper().StartsWith("Y")) break; else if (response.ToUpper().StartsWith("N")) { WriteMessage(MessageType.Routine, "Action cancelled by user.\n"); return 915; } } } if (delMissing) { foreach (CharaSet set in missing) { set.Parent.Remove(set); } using (FileStream fs = new FileStream(input, FileMode.Create)) { XmlWriter xw = XmlTextWriter.Create(fs, new XmlWriterSettings() { Encoding = Encoding.UTF8, Indent = true, IndentChars = "\t" }); resmgr.ToXml(xw); xw.Flush(); } } WriteMessage(MessageType.Routine, "Success!\n"); } else { WriteMessage(MessageType.Routine, "No missing files have been detected!\n"); } return 0; }