/// <summary> /// Returns array of bytes for file data section /// </summary> /// <param name="files"></param> /// <returns></returns> private byte[] _MakeFileData(IEnumerable <byte[]> files) { byte[] returnedData = new byte[0]; if (files != null) { // Computing theorical size: current size of each file + up to 16 padding bytes per file int maxSize = 0; foreach (byte[] anotherFileData in files) { maxSize += (anotherFileData.Length + 16); } byte[] tempData = new byte[maxSize]; long realDataSize; // Writing all files Section fileDataSection = _GetSection(SectionType.FileData); using (BinaryWriter writer = new BinaryWriter(new MemoryStream(tempData))) { int index = 0; foreach (byte[] anotherFileData in files) { // Updating packed file information PackedFile correspondingFile = _FileList[index++]; correspondingFile.fileSize = (uint)anotherFileData.Length; correspondingFile.startAddress = (uint)writer.BaseStream.Position + fileDataSection.address; // Data writer.Write(anotherFileData); // Padding... if (index < _FileList.Count) { uint paddingLength = _GetPaddingLength((uint)anotherFileData.Length, _SecondaryBlockSize); byte[] padding = new byte[paddingLength]; byte[] paddingString = String2.ToByteArray(_PADDING_SEQUENCE); Array.Copy(paddingString, padding, paddingLength); writer.Write(padding); } } realDataSize = writer.BaseStream.Position; } // Putting real data into result returnedData = new byte[realDataSize]; Array.Copy(tempData, 0, returnedData, 0, realDataSize); // Updating usableSize fileDataSection.usableSize = (uint)realDataSize; } return(returnedData); }
/// <summary> /// Convertit un fichier DDS en fichier 2DB. Utilise l'en-tête du fichier 2DB d'origine. /// </summary> /// <param name="original2DBFile">ancien fichier 2DB</param> /// <param name="sourceDDSFile">fichier DDS à convertir</param> /// <param name="target2DBFile">nouveau fichier 2DB à créer</param> /// <param name="newTextureName">if not null, allows to override texture name</param> /// <param name="mipmapCount">if not -1, forces mipmap count</param> public static void DDSTo2DB(string original2DBFile, string sourceDDSFile, string target2DBFile, string newTextureName, int mipmapCount) { // EVO_37 : 1. Récupérer les dimensions de la texture DDS ddsFile = (DDS)TduFile.GetFile(sourceDDSFile); DDS.TextureHeader ddsHeader = (DDS.TextureHeader)ddsFile.Header; uint ddsWidth = ddsHeader.ddsd.dwWidth; uint ddsHeight = ddsHeader.ddsd.dwHeight; uint ddsMipmapCount = ddsHeader.ddsd.dwMipMapCount; // 2. Prendre l'en-tête du fichier 2DB original _2DB old2DBFile = (_2DB)TduFile.GetFile(original2DBFile); _2DB.TextureHeader old2DBHeader = (_2DB.TextureHeader)old2DBFile.Header; // 3. Mettre à jour les dimensions et nombre de mipmap old2DBHeader.height = (short)ddsHeight; old2DBHeader.width = (short)ddsWidth; // BUG_58 : mipmap count in 2DB takes main texture into account (so always >= 1) // Mipmap count enforcement if (mipmapCount == KEEP_ORIGINAL_MIPMAP_COUNT) { old2DBHeader.bMipMapCount = old2DBHeader.bMipMapCountBis = (byte)(ddsMipmapCount + 1); } else { old2DBHeader.bMipMapCount = old2DBHeader.bMipMapCountBis = (byte)mipmapCount; } // 4. Calcul de bourrage / découpage éventuels long fileSize = _2DB.HEADER_SIZE + (ddsFile.Size - DDS.HEADER_SIZE) + _2DB.FINALIZATION_STRING.LongLength; // 5. Création du nouveau fichier et assemblage using (BinaryWriter writer = new BinaryWriter(new FileStream(target2DBFile, FileMode.Create, FileAccess.Write))) { // Mettre à jour la taille du fichier dans l'entete old2DBHeader.dwSize = (uint)fileSize; old2DBHeader.dwSize2 = old2DBHeader.dwSize2Bis = (uint)(fileSize - 32); // Override texture name ? if (newTextureName != null) { old2DBHeader.strName = String2.ToByteArray(Tools.NormalizeName(newTextureName)); } // Ecriture de l'en-tête old2DBFile.Header = old2DBHeader; writer.Write(old2DBFile.HeaderData); // Data writing byte[] imageData = ddsFile.ImageData; writer.Write(imageData); // Finalization: REAL (??) writer.Write(_2DB.FINALIZATION_STRING); } }
/// <summary> /// Writes header section into a byte array /// </summary> private byte[] _WriteHeaderSection() { // Max length = 64 bytes int dataLength = 64; byte[] tempData = new byte[dataLength]; using (BinaryWriter dataWriter = new BinaryWriter(new MemoryStream(tempData))) { // TAG dataWriter.Write(String2.ToByteArray(_BANK_TAG)); // 0 data dataWriter.Seek(0x8, SeekOrigin.Current); // Special flags dataWriter.Write(_SpecialFlag1); dataWriter.Write(_SpecialFlag2); // BNK file size dataWriter.Write(_GetTotalSize()); // Packed content size (% 0x10) dataWriter.Write(PackedContentSize); // TODO 2x4 octets .... dataWriter.Write(_MainBlockSize); dataWriter.Write(_SecondaryBlockSize); // Packed files count dataWriter.Write(PackedFilesCount); // Year dataWriter.Write((uint)DateTime.Today.Year); // Section addresses uint fileSizeSectionAddress = _GetSection(SectionType.FileSize).address; uint typeMappingSectionAddress = _GetSection(SectionType.TypeMapping).address; uint fileNameSectionAddress = _GetSection(SectionType.FileName).address; uint fileOrderSectionAddress = _GetSection(SectionType.FileOrder).address; uint fileDataSectionAddress = _GetSection(SectionType.FileData).address; dataWriter.Write(fileSizeSectionAddress); dataWriter.Write(typeMappingSectionAddress); dataWriter.Write(fileNameSectionAddress); dataWriter.Write(fileOrderSectionAddress); dataWriter.Write(_Unknown); dataWriter.Write(fileDataSectionAddress); // Data size dataLength = (int)dataWriter.BaseStream.Position; } byte[] returnedData = new byte[dataLength]; Array.Copy(tempData, returnedData, dataLength); return(returnedData); }
/// <summary> /// Utility method returning internal view name from a clean one + custom properties (if necessary) /// </summary> /// <param name="currentView"></param> /// <returns></returns> private static byte[] _SetViewName(View currentView) { byte[] returnedBytes = new byte[16]; if (currentView.isValid) { string currentViewName = currentView.name; int index = 0; foreach (char anotherCharacter in currentViewName) { returnedBytes[index] = (byte)anotherCharacter; index++; } // Finalization of string returnedBytes[index] = (byte)'\0'; index++; // Properties for custom camera, if there's enough place to store them... if (currentView.parentCameraId != 0 && index <= 8) { // Parent id string parentCameraId = currentView.parentCameraId.ToString(); byte[] idArray = String2.ToByteArray(parentCameraId); Array.Copy(idArray, 0, returnedBytes, index, idArray.Length); index += idArray.Length; // Separator returnedBytes[index] = (byte)Tools.SYMBOL_VALUE_SEPARATOR; index++; // Parent type string parentType = ((int)currentView.parentType).ToString(); byte[] typeArray = String2.ToByteArray(parentType); Array.Copy(typeArray, 0, returnedBytes, index, typeArray.Length); index += typeArray.Length; } // Padding for (int i = index; i < 16; i++) { returnedBytes[i] = 0xCD; } } return(returnedBytes); }
/// <summary> /// Writes specified file list node /// </summary> /// <param name="bnkWriter"></param> /// <param name="nodeToWrite"></param> /// <returns></returns> private static void _WriteFileNode(BinaryWriter bnkWriter, FileInfoNode nodeToWrite) { if (bnkWriter != null && nodeToWrite != null && bnkWriter.BaseStream.Position < bnkWriter.BaseStream.Length) { // Current node switch (nodeToWrite.type) { case FileInfoNodeType.File: bnkWriter.Write((byte)nodeToWrite.name.Length); break; case FileInfoNodeType.Folder: case FileInfoNodeType.ExtensionGroup: byte nodeFlag = (byte)(_LIST_FOLDER_NODE_FLAG - nodeToWrite.name.Length); bnkWriter.Write(nodeFlag); bnkWriter.Write((byte)nodeToWrite.childNodes.Count); break; } byte[] currentName = String2.ToByteArray(nodeToWrite.name); bnkWriter.Write(currentName); // Writing child nodes if (nodeToWrite.childNodes != null) { foreach (FileInfoNode anotherChildNode in nodeToWrite.childNodes) { _WriteFileNode(bnkWriter, anotherChildNode); } } } }
/// <summary> /// Generates specified section contents into a byte array /// </summary> /// <param name="type"></param> private byte[] _WriteSection(SectionType type) { Section sectionToWrite = _GetSection(type); byte[] returnedData = new byte[0]; // Modification différente selon le type de section switch (type) { case SectionType.Header: #region HEADER sectionToWrite.data = _WriteHeaderSection(); break; #endregion case SectionType.FileSize: #region INFOS SUR LE CONTENU sectionToWrite.data = _WriteContentInfoSection(); break; #endregion case SectionType.TypeMapping: #region UNKNOWN SECTION // Original contents are kept break; #endregion case SectionType.FileName: #region LISTE DE NOMS DE FICHIERS // New! all data is now rewritten for this section sectionToWrite.data = _WriteFileListSection(); break; #endregion case SectionType.FileOrder: #region ORDRE DES FICHIERS // Original contents are kept sectionToWrite.data = _WriteFileOrderSection(); break; #endregion case SectionType.FileData: #region PACKED FILES // Contents already updated returnedData = sectionToWrite.data; break; #endregion default: throw new Exception("Writing " + type + " section is not supported here."); } if (SectionType.FileData == sectionToWrite.sectionType) { return(returnedData); } // New length update sectionToWrite.usableSize = (uint)sectionToWrite.data.Length; // Calcul du bourrage sectionToWrite.paddingSize = _GetPaddingLength(_PREDATA_LENGTH + (uint)sectionToWrite.data.Length, _MainBlockSize); // New size and checksum byte[] preData = new byte[_PREDATA_LENGTH]; using (BinaryWriter preDataWriter = new BinaryWriter(new MemoryStream(preData))) { preDataWriter.Write((ushort)sectionToWrite.usableSize); preDataWriter.BaseStream.Seek(0x2, SeekOrigin.Current); preDataWriter.Write(sectionToWrite.Checksum); } // Padding calculation byte[] padding = new byte[0]; if (type != SectionType.FileData) { string paddingString = _PADDING_SEQUENCE.Substring(0, (int)sectionToWrite.paddingSize); padding = String2.ToByteArray(paddingString); } // Writing int dataLength = 0; if (sectionToWrite.data != null) { dataLength = sectionToWrite.data.Length; } returnedData = new byte[preData.Length + dataLength + padding.Length]; Array.Copy(preData, returnedData, preData.Length); if (dataLength != 0) { Array.Copy(sectionToWrite.data, 0, returnedData, preData.Length, dataLength); } Array.Copy(padding, 0, returnedData, preData.Length + dataLength, padding.Length); return(returnedData); }
/// <summary> /// Convertit un fichier DDS en fichier 2DB (EVO_37). Génère un nouvel en-tête. /// </summary> /// <param name="sourceDDSFile">fichier DDS à convertir</param> /// <param name="target2DBFile">nouveau fichier 2DB à créer</param> /// <param name="newTextureName">if not null, allows to override texture name</param> /// <param name="mipmapCount">if not -1, forces mipmap count</param> public static void DDSTo2DB(string sourceDDSFile, string target2DBFile, string newTextureName, int mipmapCount) { if (sourceDDSFile != null && target2DBFile != null) { DDS ddsFile = (DDS)TduFile.GetFile(sourceDDSFile); DDS.TextureHeader ddsHeader = (DDS.TextureHeader)ddsFile.Header; // Construction d'un fichier 2DB flambant neuf FileInfo fi = new FileInfo(target2DBFile); _2DB new2DBFile = (_2DB)TduFile.GetFile(target2DBFile); _2DB.TextureHeader newHeader = new _2DB.TextureHeader(); string picName; // Override texture name ? if (newTextureName != null) { picName = Tools.NormalizeName(newTextureName); } else { picName = Tools.NormalizeName(fi.Name.ToUpper()); } // Valeurs connues newHeader.bID1 = String2.ToByteArray(_2DB.ID1_STRING); newHeader.bID2 = String2.ToByteArray(_2DB.ID2_STRING); newHeader.dwTwo = 2; newHeader.dwZero1 = newHeader.dwZero2 = newHeader.dwZero3 = 0; newHeader.one = 1; newHeader.height = (short)ddsHeader.ddsd.dwHeight; newHeader.width = (short)ddsHeader.ddsd.dwWidth; newHeader.strName = String2.ToByteArray(picName); // BUG_58 : mipmap count in 2DB takes main texture into account (so always >= 1) uint ddsMipmapCount = ddsHeader.ddsd.dwMipMapCount; // Mipmap count enforcement if (mipmapCount == KEEP_ORIGINAL_MIPMAP_COUNT) { newHeader.bMipMapCount = newHeader.bMipMapCountBis = (byte)(ddsMipmapCount + 1); } else { newHeader.bMipMapCount = newHeader.bMipMapCountBis = (byte)mipmapCount; } // Format & type (linked ??) byte format; uint type; switch (ddsHeader.ddsd.ddpfPixelFormat.dwFourCC) { case DDS.FORMAT_FOURCC_DXT1: format = _2DB.FORMAT_ID_B1; type = _2DB.TYPE_DXT1; break; case DDS.FORMAT_FOURCC_DXT5: format = _2DB.FORMAT_ID_B5; type = _2DB.TYPE_DXT5; break; case DDS.FORMAT_FOURCC_UNKNOWN: format = _2DB.FORMAT_ID_BARGB8; type = _2DB.TYPE_ARGB8; break; default: // Log warning Log.Warning("Unsupported texture format: '" + ddsHeader.ddsd.ddpfPixelFormat.dwFourCC + "'. Treated as DXT5."); // DXT5 format = _2DB.FORMAT_ID_B5; type = _2DB.TYPE_DXT5; break; } newHeader.bFormat = format; newHeader.dwType = type; // TODO A découvrir... et initialiser plus tard newHeader.dwFlags = 512; newHeader.dwUnk6 = 0; newHeader.dwUnk7 = 0; newHeader.bUnk2 = 0; newHeader.unk3 = 0; newHeader.dwUnk4 = 0; // Derniers calculs : taille du fichier uint _2dbSize = (uint)(_2DB.HEADER_SIZE + ddsFile.ImageData.Length + _2DB.FINALIZATION_STRING.Length); newHeader.dwSize = _2dbSize; newHeader.dwSize2 = newHeader.dwSize2Bis = _2dbSize - 32; // Ecriture des sections try { using (BinaryWriter writer = new BinaryWriter(new FileStream(target2DBFile, FileMode.Create, FileAccess.Write))) { // 1. En-tête new2DBFile.Header = newHeader; writer.Write(new2DBFile.HeaderData); // 2. Données d'image writer.Write(ddsFile.ImageData); // Finalisation writer.Write(_2DB.FINALIZATION_STRING); } } catch (IOException ioe) { Exception2.PrintStackTrace(ioe); throw; } } }