static void Main(string[] args) { // If there are no arguments passed then it acts as if help command is ran but through an invalid usage if (args.Count() == 0) { Program.ShowHelpText(true); return; } switch (args[0].ToLower()) { default: { // Allows for drag & drop of file/folders to be converted if (args.Count() == 1) { if (File.Exists(args[0])) { string input = args[0]; args = new string[3]; args[0] = string.Empty; // Can equal blank string value args[1] = "-in"; args[2] = input; goto case "-c"; } } Program.ShowHelpText(true); return; } case "-h": case "-help": { Program.ShowHelpText(); return; } case "-c": case "-convert": { string input = null; string output = null; for (uint i = 1; i < args.Count(); i++) { string option = args[i]; switch (option) { default: { Program.ShowHelpText(true); return; } case "-in": { i++; input = args[i]; break; } case "-out": { i++; output = args[i]; break; } } } if (string.IsNullOrEmpty(input)) { Program.ShowHelpText(true); return; } Console.WriteLine("Converting " + Path.GetFileName(input) + "..."); NormalMap.ConvertToPS3(input, string.IsNullOrEmpty(output) ? input : output); Console.WriteLine("\nDone!\n"); break; } } }
/// <summary> /// Writes BSA for Oblivion PS3. /// </summary> /// <param name="path">Real filesystem path to write BSA to.</param> /// <param name="assetList">List of <see cref="Asset"/> to write in BSA.</param> /// <param name="compress">True or false if assets in BSA should be compressed.</param> /// <param name="usePS3FileFlags">True or false if PS3 file flags should be used.</param> /// <param name="extendDDS">True or false if DDS texture file data should be extended.</param> /// <param name="convertNormalMaps">True or false if normal maps should be converted to PS3.</param> public static void Write(string path, List <Asset> assetList, bool compress, bool usePS3FileFlags, bool extendDDS, bool convertNormalMaps) { var folderDict = new Dictionary <string, List <Asset> >(); var extList = new List <string>(); uint totalFolderNameLength = 0; uint totalFileNameLength = 0; // Sets up all assets to be in folderDict foreach (Asset asset in assetList) { string folderName = Path.GetDirectoryName(asset.EntryStr); if (!folderDict.ContainsKey(folderName)) { folderDict.Add(folderName, new List <Asset>()); totalFolderNameLength += (uint)folderName.Length + 1; // Includes null terminator, not prefixed length sbyte } totalFileNameLength += (uint)asset.FileName.Length + 1; // Includes null terminator folderDict[folderName].Add(asset); if (!extList.Contains(asset.Extension)) { extList.Add(asset.Extension); } } // Begins writing of archive using (var writer = new BinaryWriter(File.Open(path, FileMode.Create, FileAccess.Write, FileShare.Read))) { writer.Write(0x00415342); // "BSA" + 0x00, archive magic writer.Write(103); // Version writer.Write(36); // Folder records offset writer.Write(compress ? 0x00000707 : 0x00000703); writer.Write(folderDict.Count()); writer.Write(assetList.Count()); writer.Write(totalFolderNameLength); writer.Write(totalFileNameLength); writer.Write(BSA.GetFileFlags(extList, usePS3FileFlags)); // Texture archive file flags var folderOrdList = new List <string>(); // Keeps order of folders, with folder names var folderOffList = new List <uint>(); // Loops through the folder dictionary, while also organizing folders to follow Oblivion BSA rules foreach (KeyValuePair <string, List <Asset> > folder in folderDict.OrderBy(folder => OblivionBSAHash.GetPC(folder.Key))) { folderOrdList.Add(folder.Key); folder.Value.Sort((a, b) => a.Hash.CompareTo(b.Hash)); // Assets in folder sorted to follow Oblivion BSA rules writer.Write(OblivionBSAHash.GetPC(folder.Key)); writer.Write(folder.Value.Count()); writer.Write(-1); // Temporary, file record offset } foreach (string folder in folderOrdList) { folderOffList.Add((uint)writer.BaseStream.Position); writer.Write((byte)(folder.Length + 1)); writer.Write(folder.ToCharArray()); writer.Write((byte)0); foreach (Asset asset in folderDict[folder]) { writer.Write(asset.Hash); writer.Write((long)-1); // Temporary, asset size + offset } } foreach (string folder in folderOrdList) { foreach (Asset asset in folderDict[folder]) { writer.WriteNullTerminatedString(asset.FileName); } } foreach (string folder in folderOrdList) { foreach (Asset asset in folderDict[folder]) { asset.Offset = (uint)writer.BaseStream.Position; byte[] data; if (convertNormalMaps && asset.IsDDS && asset.IsNormalMap) { string tempPath = Path.GetTempFileName(); bool converted = NormalMap.ConvertToPS3(asset.RealPath, tempPath); data = File.ReadAllBytes(converted ? tempPath : asset.RealPath); File.Delete(tempPath); // Cleans up } else { data = File.ReadAllBytes(asset.RealPath); } bool extendAsset = extendDDS && asset.IsDDS && !asset.IsExtended; if (compress) { asset.OriginalSize = extendAsset ? (uint)data.Length + (uint)asset.EntryStr.Length + 1 : (uint)data.Length; writer.Write(asset.OriginalSize); writer.Write(BSA.GetCompressedZlibData(data, asset, extendAsset)); } else { asset.Size = (uint)data.Length; writer.Write(data); if (extendAsset) { asset.Size += (uint)asset.EntryStr.Length + 1; // +1 is accounting for null terminator writer.Write(asset.EntryStr); } } } } writer.BaseStream.Position = 36; // Folder record offset foreach (uint off in folderOffList) { writer.BaseStream.Position += 12; // Skips hash and file count writer.Write(off + totalFileNameLength); } foreach (string folder in folderOrdList) { writer.BaseStream.Position += folder.Length + 2; foreach (Asset asset in folderDict[folder]) { writer.BaseStream.Position += 8; // Skips hash writer.Write(asset.Size); writer.Write(asset.Offset); } } } }