public static void UnpackMap(string inputMapPath, string outputFolderPath) { if (!File.Exists(inputMapPath)) { throw new FileNotFoundException(String.Format("Input map at {0} does not exist.", inputMapPath)); } if (Directory.Exists(outputFolderPath) && Directory.EnumerateFileSystemEntries(outputFolderPath).Any()) { throw new IOException(String.Format("Output folder at {0} is not empty.", outputFolderPath)); } // Strategy is as follows: // Copy map to the target location. Then extract all files, but convert the map into a container by deleting all the files inside. // Goal is that we are left with the MPQ container which contains all the other misc information which we are currently unable to manage. // In future, we might be able to generate the MPQ from scratch, but for now we will keep a big chunk of "unmanaged" data and focus // on the files we are able to manage. string containerPath = Path.Combine(outputFolderPath, FILENAME_CONTAINER); Directory.CreateDirectory(outputFolderPath); // Copy map to target location in preparation to convert it into a container File.Copy(inputMapPath, containerPath); string listFile = null; // Extract all files from the map and remove them from the container using (MpqArchive container = new MpqArchive(containerPath, FileAccess.ReadWrite)) using (MpqArchive archive = new MpqArchive(inputMapPath, FileAccess.Read)) { // Check if somebody is trying to unpack a container - lets avoid creating Matroshchkas if (archive.HasFile(DUMMY_FILENAME)) { throw new Exception("Input map seems to be a container rather than a packed map."); } using (MpqFileStream file = archive.OpenFile(MPQ_LISTFILE)) using (StreamReader sr = new StreamReader(file)) { listFile = sr.ReadToEnd(); } // We have to add a dummy file as otherwise the listfile gets deleted and somehow doesnt get restored... if (!container.HasFile(DUMMY_FILENAME)) { string dummyFilePath = Path.GetTempFileName(); try { using (StreamWriter listFileHandle = new StreamWriter(dummyFilePath)) { listFileHandle.WriteLine("Dummy file"); } container.AddFileFromDiskEx(dummyFilePath, DUMMY_FILENAME, MpqFileAddFileExFlags.MPQ_FILE_COMPRESS, MpqFileAddFileExCompression.MPQ_COMPRESSION_BZIP2, MpqFileAddFileExCompression.MPQ_COMPRESSION_NEXT_SAME); } finally { File.Delete(dummyFilePath); } } using (StringReader reader = new StringReader(listFile)) { string currentArchiveFile = string.Empty; do { currentArchiveFile = reader.ReadLine(); if (currentArchiveFile != null) { if (currentArchiveFile == DUMMY_FILENAME) { continue; } string targetPath = Path.Combine(outputFolderPath, currentArchiveFile.Trim('\\')); Directory.CreateDirectory(Path.GetDirectoryName(targetPath)); archive.ExtractFile(currentArchiveFile, targetPath); container.RemoveFile(currentArchiveFile); } } while (currentArchiveFile != null); } container.Compact(MPQ_LISTFILE); } }
public static void PackMap(string inputFolderPath, string outputMapPath, List <MapPublishingApp.PostProcessing.IPostProcessor> postProcessors) { if (postProcessors == null) { postProcessors = new List <PostProcessing.IPostProcessor>(); } if (!Directory.Exists(inputFolderPath)) { throw new FileNotFoundException(String.Format("Input folder at {0} does not exist.", inputFolderPath)); } var inputContainerPath = Path.Combine(inputFolderPath, FILENAME_CONTAINER); if (!File.Exists(inputContainerPath)) { throw new InvalidDataException("The input container does not exist. Are you sure you're pointing to a directory which has been unpacked using this tool?"); } if (File.Exists(outputMapPath)) { // TODO: Add argument to allow overwriting. throw new IOException("A map at the target path already exists"); } // Copy container to target location File.Copy(inputContainerPath, outputMapPath); using (MpqArchive archive = new MpqArchive(outputMapPath, FileAccess.ReadWrite)) { List <string> files = new List <string>(); foreach (string file in Directory.GetFiles(inputFolderPath, "*", SearchOption.AllDirectories)) { string relativePath = new Uri(inputFolderPath.Trim(Path.DirectorySeparatorChar) + Path.DirectorySeparatorChar).MakeRelativeUri(new Uri(file)).ToString(); // Paths in listfile are with backslashes relativePath = relativePath.Replace('/', '\\'); if (relativePath.Contains(FILENAME_CONTAINER)) { continue; } // Multiplayer folder starts with special character if (relativePath.StartsWith("Multiplayer")) { relativePath = "\\" + relativePath; } files.Add(relativePath); // Binary files can not be properly processed by the StreamWriter - it will modify them. Thus, only process text files if (POSTPROCESSABLE_EXTENSIONS.Contains(Path.GetExtension(file))) { var tempFilePath = Path.GetTempFileName(); using (var tempFileStream = new StreamWriter(new FileStream(tempFilePath, FileMode.Open, FileAccess.ReadWrite, FileShare.Read, 4096))) { StringBuilder sb = new StringBuilder(File.ReadAllText(file)); // Execute preprocessors foreach (var postprocessor in postProcessors) { postprocessor.Process(relativePath, ref sb); } tempFileStream.Write(sb); } archive.AddFileFromDiskEx(tempFilePath, relativePath, MpqFileAddFileExFlags.MPQ_FILE_COMPRESS | MpqFileAddFileExFlags.MPQ_FILE_REPLACEEXISTING, MpqFileAddFileExCompression.MPQ_COMPRESSION_BZIP2, MpqFileAddFileExCompression.MPQ_COMPRESSION_NEXT_SAME); File.Delete(tempFilePath); } else { archive.AddFileFromDiskEx(file, relativePath, MpqFileAddFileExFlags.MPQ_FILE_COMPRESS | MpqFileAddFileExFlags.MPQ_FILE_REPLACEEXISTING, MpqFileAddFileExCompression.MPQ_COMPRESSION_BZIP2, MpqFileAddFileExCompression.MPQ_COMPRESSION_NEXT_SAME); } } archive.RemoveFile(DUMMY_FILENAME); archive.Compact(MPQ_LISTFILE); } }
/** * Runs the program */ private void Run(String[] args) { // Verify argument count if (!CheckArguments(args)) { return; } // Retrieve argument values and listfile, and check for existence String operationName = args[0]; String mpqName = args[1]; String listfileName = "listfile.txt"; if (!File.Exists(mpqName)) { Console.WriteLine("MPQ file does not exist: " + mpqName); return; } if (!File.Exists(listfileName)) { Console.WriteLine("Listfile does not exist: " + listfileName); return; } // Determine what operation the user wanted Boolean extract; if (operationName.Equals("ext") || operationName.Equals("exp") || operationName.Equals("export") || operationName.Equals("extract")) { extract = true; } else if (operationName.Equals("imp") || operationName.Equals("import")) { extract = false; } else { Console.WriteLine("Invalid operation: " + operationName); return; } // For every entered file, we either perform import/export using (MpqArchive archive = new MpqArchive(mpqName, FileAccess.ReadWrite)) { // Add listfile.txt as external listfile int retval = archive.AddListFile(listfileName); Console.WriteLine("Added listfile: " + retval + " (0 is ok)"); for (int i = 2; i < args.Length; i++) { if (extract) { Extract(args[i], archive); } else { Import(args[i], archive); } } Console.WriteLine("All desired operations completed. Flushing & closing."); // Close out resources archive.Compact(listfileName); archive.Flush(); archive.Dispose(); } }