using System; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.IO.Compression; using System.Runtime.InteropServices; using System.Security; using System.Text; using System.Windows; using System.Windows.Controls; using System.Windows.Input; using Microsoft.Win32; using Microsoft.WindowsAPICodePack.Dialogs; using RageLib.Archives; using RageLib.GTA5.Archives; using RageLib.GTA5.ArchiveWrappers; using RageLib.GTA5.Cryptography; using RageLib.GTA5.Utilities; namespace CarConverter { /// /// Interaction logic for MainWindow.xaml /// public partial class MainWindow { private DirectoryInfo root; public MainWindow() { InitializeComponent(); var path = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) + @"\CarConverter\"; if (Directory.Exists(path)) OutputDir.Content = File.ReadAllText(path + "DefaultOutput.penis"); //ConsoleManager.Show(); } private void OnRPFDrop(object sender, DragEventArgs e) { Clear(null, null); var files = e.Data.GetData(DataFormats.FileDrop) as string[]; if (files.Length != 1) { MessageBox.Show("Du kannst nur eine Datei gleichzeitig Decompilen"); } else { root = new DirectoryInfo(DecompileRpfFile(new FileInfo(files[0]))); TreeViewItem item = new TreeViewItem(); item.Header = new FileInfo(files[0]).Name; item.Tag = root; item.FontWeight = FontWeights.Normal; FoldersItem.Items.Add(item); PlaceFilesInTree(item, root); LoadFiles(root); } } private void PlaceFilesInTree(TreeViewItem parent, DirectoryInfo info) { foreach (DirectoryInfo directoryInfo in info.GetDirectories()) { TreeViewItem p = new TreeViewItem(); p.Header = directoryInfo.Name; p.Tag = directoryInfo; p.FontWeight = FontWeights.Normal; parent.Items.Add(p); PlaceFilesInTree(p, directoryInfo); } foreach (FileInfo file in info.GetFiles()) { TreeViewItem item = new TreeViewItem(); item.Header = file.Name; item.Tag = file; item.FontWeight = FontWeights.Normal; parent.Items.Add(item); } } private string DecompileRpfFile(FileInfo fileInfo) { try { var outFolder = Path.GetTempPath() + @"CarConverter\Decompiled\"; if (Directory.Exists(outFolder)) Directory.Delete(outFolder, true); var fileStream = new FileStream(fileInfo.FullName, FileMode.Open); var inputArchive = RageArchiveWrapper7.Open(fileStream, fileInfo.Name); var queue = new List>() { new Tuple(fileInfo.FullName, inputArchive, false) }; while (queue.Count > 0) { var fullPath = queue[0].Item1; var rpf = queue[0].Item2; var isTmpStream = queue[0].Item3; queue.RemoveAt(0); ArchiveUtilities.ForEachFile(fullPath.Replace(fileInfo.FullName, ""), rpf.Root, rpf.archive_.Encryption, (fullFileName, file, encryption) => { string path = outFolder + fullFileName; string dir = Path.GetDirectoryName(path); if (!Directory.Exists(dir)) Directory.CreateDirectory(dir); Console.WriteLine(fullFileName); if (file.Name.EndsWith(".rpf")) { try { var tmpStream = new FileStream(Path.GetTempFileName(), FileMode.Open); file.Export(tmpStream); RageArchiveWrapper7 archive = RageArchiveWrapper7.Open(tmpStream, file.Name); queue.Add(new Tuple(fullFileName, archive, true)); } catch (Exception e) { Console.WriteLine(e); } } else { if (file.Name.EndsWith(".xml") || file.Name.EndsWith(".meta")) { byte[] data = GetBinaryFileData((IArchiveBinaryFile)file, encryption); string xml; if (data[0] == 0xEF && data[1] == 0xBB && data[2] == 0xBF) // Detect BOM { xml = Encoding.UTF8.GetString(data, 3, data.Length - 3); } else { xml = Encoding.UTF8.GetString(data); } File.WriteAllText(path, xml, Encoding.UTF8); } else { file.Export(path); } } }); var stream = (FileStream)rpf.archive_.BaseStream; string fileName = stream.Name; rpf.Dispose(); if (isTmpStream) { File.Delete(fileName); } } return outFolder; } catch (Exception e) { Console.WriteLine(e); } return ""; } private static byte[] GetBinaryFileData(IArchiveBinaryFile file, RageArchiveEncryption7 encryption) { using (var ms = new MemoryStream()) { file.Export(ms); byte[] data = ms.ToArray(); if(file.IsEncrypted) { if (encryption == RageArchiveEncryption7.AES) { data = GTA5Crypto.DecryptAES(data); } else // if(encryption == RageArchiveEncryption7.NG) { data = GTA5Crypto.DecryptNG(data, file.Name, (uint)file.UncompressedSize); } } if (file.IsCompressed) { using (var dfls = new DeflateStream(new MemoryStream(data), CompressionMode.Decompress)) { using (var outstr = new MemoryStream()) { dfls.CopyTo(outstr); data = outstr.ToArray(); } } } return data; } } private void LoadFiles(DirectoryInfo directory) { DirectoryInfo metaFolder = new DirectoryInfo(directory.FullName + @"data\"); foreach (FileInfo file in metaFolder.GetFiles()) { AddMetaFile(file); } DirectoryInfo streamFolder = new DirectoryInfo(directory.FullName + @"x64\vehicles.rpf"); foreach (FileInfo file in streamFolder.GetFiles()) { AddStreamFile(file); } DirectoryInfo modsFolder = new DirectoryInfo(directory.FullName + @"x64\vehiclemods"); if (modsFolder.Exists) { foreach (DirectoryInfo folder in modsFolder.GetDirectories()) { foreach (FileInfo file in folder.GetFiles()) { AddStreamFile(file); } } } if (StreamFiles.Items.Count > 0) CarName.Text = ((StreamFiles.Items[0] as TreeViewItem).Tag as FileInfo).Name.Split('.')[0]; } private void AddMetaFile(FileInfo file) { TreeViewItem item = new TreeViewItem(); item.Header = file.Name; item.Tag = file; item.FontWeight = FontWeights.Normal; MetaFiles.Items.Add(item); } private void AddStreamFile(FileInfo file) { TreeViewItem item = new TreeViewItem(); item.Header = file.Name; item.Tag = file; item.FontWeight = FontWeights.Normal; StreamFiles.Items.Add(item); } private void OnImplement(object sender, MouseButtonEventArgs e) { TreeViewItem item = FoldersItem.SelectedItem as TreeViewItem; if (item == null) return; if (item.Tag is FileInfo) { FileInfo file = item.Tag as FileInfo; if (file is null) return; if (file.Name.EndsWith(".meta")) { AddMetaFile(file); } else { AddStreamFile(file); } } } private void OnRemove(object sender, MouseButtonEventArgs e) { TreeView view = sender as TreeView; if (view is null) return; view.Items.Remove(view.SelectedItem); } private void Clear(object sender, RoutedEventArgs e) { FoldersItem.Items.Clear(); MetaFiles.Items.Clear(); StreamFiles.Items.Clear(); root = null; } private void Build(object sender, RoutedEventArgs e) { try { var outputPath = OutputDir.Content + @"\" + CarName.Text; if (Directory.Exists(outputPath)) Directory.Delete(outputPath, true); DirectoryInfo info = Directory.CreateDirectory(outputPath); File.WriteAllBytes(info.FullName + @"\__resource.lua", GetResourceBytes()); DirectoryInfo streamFolder = info.CreateSubdirectory("stream"); foreach (var item in MetaFiles.Items) { FileInfo file = (item as TreeViewItem).Tag as FileInfo; file.CopyTo(info.FullName + @"\" + file.Name); } foreach (var item in StreamFiles.Items) { FileInfo file = (item as TreeViewItem).Tag as FileInfo; file.CopyTo(streamFolder.FullName + @"\" + file.Name); } MessageBox.Show("Fertig du Pimmelberger"); } catch (Exception exception) { Console.WriteLine(exception); } } private byte[] GetResourceBytes() { return Encoding.ASCII.GetBytes("resource_manifest_version '77731fab-63ca-442c-a67b-abc70f28dfa5'\n\n" + "files {\n" + " 'carcols.meta',\n" + " 'vehicles.meta',\n" + " 'carvariations.meta',\n" + " 'handling.meta',\n" + "}\n\n" + "data_file 'CARCOLS_FILE' 'carcols.meta'\n" + "data_file 'HANDLING_FILE' 'handling.meta'\n" + "data_file 'VEHICLE_METADATA_FILE' 'vehicles.meta'\n" + "data_file 'VEHICLE_VARIATION_FILE' 'carvariations.meta'\n"); } private void ChooseOutput(object sender, RoutedEventArgs e) { var dialog = new CommonOpenFileDialog(); dialog.Multiselect = false; dialog.IsFolderPicker = true; if (dialog.ShowDialog(this) == CommonFileDialogResult.Ok) { OutputDir.Content = dialog.FileName; //Save Output dir var path = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) + @"\CarConverter\"; if (!Directory.Exists(path)) Directory.CreateDirectory(path); File.WriteAllText(path + "DefaultOutput.penis", OutputDir.Content as string); } } } [SuppressUnmanagedCodeSecurity] public static class ConsoleManager { private const string Kernel32DllName = "kernel32.dll"; [DllImport(Kernel32DllName)] private static extern bool AllocConsole(); [DllImport(Kernel32DllName)] private static extern bool FreeConsole(); [DllImport(Kernel32DllName)] private static extern IntPtr GetConsoleWindow(); [DllImport(Kernel32DllName)] private static extern int GetConsoleOutputCP(); public static bool HasConsole { get { return GetConsoleWindow() != IntPtr.Zero; } } /// /// Creates a new console instance if the process is not attached to a console already. /// public static void Show() { //#if DEBUG if (!HasConsole) { AllocConsole(); InvalidateOutAndError(); } //#endif } /// /// If the process has a console attached to it, it will be detached and no longer visible. Writing to the System.Console is still possible, but no output will be shown. /// public static void Hide() { //#if DEBUG if (HasConsole) { SetOutAndErrorNull(); FreeConsole(); } //#endif } public static void Toggle() { if (HasConsole) { Hide(); } else { Show(); } } static void InvalidateOutAndError() { Type type = typeof(System.Console); System.Reflection.FieldInfo _out = type.GetField("_out", System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.NonPublic); System.Reflection.FieldInfo _error = type.GetField("_error", System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.NonPublic); System.Reflection.MethodInfo _InitializeStdOutError = type.GetMethod("InitializeStdOutError", System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.NonPublic); Debug.Assert(_out != null); Debug.Assert(_error != null); Debug.Assert(_InitializeStdOutError != null); _out.SetValue(null, null); _error.SetValue(null, null); _InitializeStdOutError.Invoke(null, new object[] { true }); } static void SetOutAndErrorNull() { Console.SetOut(TextWriter.Null); Console.SetError(TextWriter.Null); } } }