/* Save out our material mappings archive */ public override PAKReturnType Save() { try { //Re-write out to the PAK ExtraBinaryUtils BinaryUtils = new ExtraBinaryUtils(); BinaryWriter ArchiveWriter = new BinaryWriter(File.OpenWrite(FilePathPAK)); ArchiveWriter.BaseStream.SetLength(0); ArchiveWriter.Write(FileHeaderJunk); ArchiveWriter.Write(MaterialMappingEntries.Count); foreach (EntryMaterialMappingsPAK ThisMatRemap in MaterialMappingEntries) { ArchiveWriter.Write(ThisMatRemap.MapHeader); ArchiveWriter.Write(ThisMatRemap.MapEntryCoupleCount); ArchiveWriter.Write(ThisMatRemap.MapJunk); ArchiveWriter.Write(ThisMatRemap.MapFilename.Length); BinaryUtils.WriteString(ThisMatRemap.MapFilename, ArchiveWriter); foreach (string MaterialName in ThisMatRemap.MapMatEntries) { ArchiveWriter.Write(MaterialName.Length); BinaryUtils.WriteString(MaterialName, ArchiveWriter); } } ArchiveWriter.Close(); return(PAKReturnType.SUCCESS); } catch (IOException) { return(PAKReturnType.FAIL_COULD_NOT_ACCESS_FILE); } catch (Exception) { return(PAKReturnType.FAIL_UNKNOWN); } }
/* Replace an existing file in the TexturePAK archive */ public override PAKReturnType ReplaceFile(string PathToNewFile, string FileName) { try { //Get the texture entry & parse new DDS int EntryIndex = GetFileIndex(FileName); if (EntryIndex == -1) { return(PAKReturnType.FAIL_GENERAL_LOGIC_ERROR); //CHANGED FOR OPENCAGE } TEX4 TextureEntry = TextureEntries[EntryIndex]; DDSReader NewTexture = new DDSReader(PathToNewFile); //Currently we only apply the new texture to the "biggest", some have lower mips that we don't edit (TODO) TEX4_Part BiggestPart = TextureEntry.Texture_V2; if (BiggestPart.HeaderPos == -1 || !BiggestPart.Saved) { BiggestPart = TextureEntry.Texture_V1; } if (BiggestPart.HeaderPos == -1 || !BiggestPart.Saved) { return(PAKReturnType.FAIL_REQUEST_IS_UNSUPPORTED); //Shouldn't reach this. } //CATHODE seems to ignore texture header information regarding size, so as default, resize any imported textures to the original size. //An option is provided in the toolkit to write size information to the header (done above) however, so don't resize if that's the case. //More work needs to be done to figure out why CATHODE doesn't honour the header's size value. int OriginalLength = BiggestPart.Length; Array.Resize(ref NewTexture.DataBlock, OriginalLength); //Update our internal knowledge of the textures BiggestPart.Length = (int)NewTexture.DataBlock.Length; BiggestPart.Width = (Int16)NewTexture.Width; BiggestPart.Height = (Int16)NewTexture.Height; TextureEntry.Format = NewTexture.Format; //TODO: Update smallest here too if it exists! //Will need to be written into the PAK at "Pull PAK sections before/after V2" too - headers are handled already. //Load the BIN and write out updated BIN texture header BinaryWriter ArchiveFileBinWriter = new BinaryWriter(File.OpenWrite(FilePathBIN)); ExtraBinaryUtils BinaryUtils = new ExtraBinaryUtils(); ArchiveFileBinWriter.BaseStream.Position = TextureEntry.HeaderPos; BinaryUtils.WriteString(TextureEntry.Magic, ArchiveFileBinWriter); ArchiveFileBinWriter.Write(BitConverter.GetBytes((int)TextureEntry.Format)); ArchiveFileBinWriter.Write((TextureEntry.Texture_V2.Length == -1) ? 0 : TextureEntry.Texture_V2.Length); ArchiveFileBinWriter.Write(TextureEntry.Texture_V1.Length); ArchiveFileBinWriter.Write(TextureEntry.Texture_V1.Width); ArchiveFileBinWriter.Write(TextureEntry.Texture_V1.Height); ArchiveFileBinWriter.Write(TextureEntry.Unk_V1); ArchiveFileBinWriter.Write(TextureEntry.Texture_V2.Width); ArchiveFileBinWriter.Write(TextureEntry.Texture_V2.Height); ArchiveFileBinWriter.Write(TextureEntry.Unk_V2); ArchiveFileBinWriter.Write(TextureEntry.UnknownHeaderBytes); ArchiveFileBinWriter.Close(); //Update headers for V1+2 in PAK if they exist BinaryWriter ArchiveFileWriter = new BinaryWriter(File.OpenWrite(FilePathPAK)); BigEndianUtils BigEndian = new BigEndianUtils(); if (TextureEntry.Texture_V1.HeaderPos != -1) { ArchiveFileWriter.BaseStream.Position = TextureEntry.Texture_V1.HeaderPos; ArchiveFileWriter.Write(TextureEntry.Texture_V1.UnknownHeaderLead); ArchiveFileWriter.Write(BigEndian.FlipEndian(BitConverter.GetBytes(TextureEntry.Texture_V1.Length))); ArchiveFileWriter.Write(BigEndian.FlipEndian(BitConverter.GetBytes(TextureEntry.Texture_V1.Length))); ArchiveFileWriter.Write(TextureEntry.Texture_V1.UnknownHeaderTrail_1); ArchiveFileWriter.Write(BigEndian.FlipEndian(BitConverter.GetBytes((Int16)EntryIndex))); ArchiveFileWriter.Write(TextureEntry.Texture_V1.UnknownHeaderTrail_2); } if (TextureEntry.Texture_V2.HeaderPos != -1) { ArchiveFileWriter.BaseStream.Position = TextureEntry.Texture_V2.HeaderPos; ArchiveFileWriter.Write(TextureEntry.Texture_V2.UnknownHeaderLead); ArchiveFileWriter.Write(BigEndian.FlipEndian(BitConverter.GetBytes(TextureEntry.Texture_V2.Length))); ArchiveFileWriter.Write(BigEndian.FlipEndian(BitConverter.GetBytes(TextureEntry.Texture_V2.Length))); ArchiveFileWriter.Write(TextureEntry.Texture_V2.UnknownHeaderTrail_1); ArchiveFileWriter.Write(BigEndian.FlipEndian(BitConverter.GetBytes((Int16)EntryIndex))); ArchiveFileWriter.Write(TextureEntry.Texture_V2.UnknownHeaderTrail_2); } ArchiveFileWriter.Close(); //Pull PAK sections before/after V2 BinaryReader ArchiveFile = new BinaryReader(File.OpenRead(FilePathPAK)); byte[] PAK_Pt1 = ArchiveFile.ReadBytes(BiggestPart.StartPos); ArchiveFile.BaseStream.Position += OriginalLength; byte[] PAK_Pt2 = ArchiveFile.ReadBytes((int)ArchiveFile.BaseStream.Length - (int)ArchiveFile.BaseStream.Position); ArchiveFile.Close(); //Write the PAK back out with new content ArchiveFileWriter = new BinaryWriter(File.OpenWrite(FilePathPAK)); ArchiveFileWriter.BaseStream.SetLength(0); ArchiveFileWriter.Write(PAK_Pt1); ArchiveFileWriter.Write(NewTexture.DataBlock); ArchiveFileWriter.Write(PAK_Pt2); ArchiveFileWriter.Close(); return(PAKReturnType.SUCCESS); } catch (IOException) { return(PAKReturnType.FAIL_COULD_NOT_ACCESS_FILE); } catch (Exception) { return(PAKReturnType.FAIL_UNKNOWN); } }
/* Load the contents of an existing ModelPAK */ public override PAKReturnType Load() { if (!File.Exists(FilePathPAK)) { return(PAKReturnType.FAIL_TRIED_TO_LOAD_VIRTUAL_ARCHIVE); } /* TODO: Verify the PAK loading is a ModelPAK by BIN version number */ try { //First, parse the MTL file to find material info string PathToMTL = FilePathPAK.Substring(0, FilePathPAK.Length - 3) + "MTL"; BinaryReader ArchiveFileMtl = new BinaryReader(File.OpenRead(PathToMTL)); //Header ArchiveFileMtl.BaseStream.Position += 40; //There are some knowns here, just not required for us yet int MaterialEntryCount = ArchiveFileMtl.ReadInt16(); ArchiveFileMtl.BaseStream.Position += 2; //Skip unknown //Strings - more work will be done on materials eventually, //but taking their names for now is good enough for model export List <string> MaterialEntries = new List <string>(); string ThisMaterialString = ""; for (int i = 0; i < MaterialEntryCount; i++) { while (true) { byte ThisByte = ArchiveFileMtl.ReadByte(); if (ThisByte == 0x00) { MaterialEntries.Add(ThisMaterialString); ThisMaterialString = ""; break; } ThisMaterialString += (char)ThisByte; } } ArchiveFileMtl.Close(); //Read the header info from BIN BinaryReader ArchiveFileBin = new BinaryReader(File.OpenRead(FilePathBIN)); ArchiveFileBin.BaseStream.Position += 4; //Skip magic TableCountPt2 = ArchiveFileBin.ReadInt32(); ArchiveFileBin.BaseStream.Position += 4; //Skip unknown TableCountPt1 = ArchiveFileBin.ReadInt32(); //Skip past table 1 for (int i = 0; i < TableCountPt1; i++) { byte ThisByte = 0x00; while (ThisByte != 0xFF) { ThisByte = ArchiveFileBin.ReadByte(); } } ArchiveFileBin.BaseStream.Position += 23; //Read file list info FilenameListEnd = ArchiveFileBin.ReadInt32(); int FilenameListStart = (int)ArchiveFileBin.BaseStream.Position; //Read all file names (bytes) byte[] filename_bytes = ArchiveFileBin.ReadBytes(FilenameListEnd); //Read table 2 (skipping all unknowns for now) ExtraBinaryUtils BinaryUtils = new ExtraBinaryUtils(); for (int i = 0; i < TableCountPt2; i++) { CS2 new_entry = new CS2(); new_entry.FilenameOffset = ArchiveFileBin.ReadInt32(); new_entry.Filename = BinaryUtils.GetStringFromByteArray(filename_bytes, new_entry.FilenameOffset); ArchiveFileBin.BaseStream.Position += 4; new_entry.ModelPartNameOffset = ArchiveFileBin.ReadInt32(); new_entry.ModelPartName = BinaryUtils.GetStringFromByteArray(filename_bytes, new_entry.ModelPartNameOffset); ArchiveFileBin.BaseStream.Position += 44; new_entry.MaterialLibaryIndex = ArchiveFileBin.ReadInt32(); new_entry.MaterialName = MaterialEntries[new_entry.MaterialLibaryIndex]; ArchiveFileBin.BaseStream.Position += 8; new_entry.BlockSize = ArchiveFileBin.ReadInt32(); ArchiveFileBin.BaseStream.Position += 14; new_entry.ScaleFactor = ArchiveFileBin.ReadInt16(); //Maybe? ArchiveFileBin.BaseStream.Position += 2; new_entry.VertCount = ArchiveFileBin.ReadInt16(); new_entry.FaceCount = ArchiveFileBin.ReadInt16(); new_entry.BoneCount = ArchiveFileBin.ReadInt16(); ModelEntries.Add(new_entry); } ArchiveFileBin.Close(); //Get extra info from each header in the PAK BinaryReader ArchiveFile = new BinaryReader(File.OpenRead(FilePathPAK)); BigEndianUtils BigEndian = new BigEndianUtils(); ArchiveFile.BaseStream.Position += 32; //Skip header for (int i = 0; i < TableCountPt2; i++) { ArchiveFile.BaseStream.Position += 8; //Skip unknowns int ThisPakSize = BigEndian.ReadInt32(ArchiveFile); if (ThisPakSize != BigEndian.ReadInt32(ArchiveFile)) { //Dud entry... handle this somehow? } int ThisPakOffset = BigEndian.ReadInt32(ArchiveFile); ArchiveFile.BaseStream.Position += 14; int ThisIndex = BigEndian.ReadInt16(ArchiveFile); ArchiveFile.BaseStream.Position += 12; if (ThisIndex == -1) { continue; //Again, dud entry. Need to look into this! } //Push it into the correct entry ModelEntries[ThisIndex].PakSize = ThisPakSize; ModelEntries[ThisIndex].PakOffset = ThisPakOffset; } HeaderListEnd = (int)ArchiveFile.BaseStream.Position; //Done! ArchiveFile.Close(); return(PAKReturnType.SUCCESS); } catch (IOException) { return(PAKReturnType.FAIL_COULD_NOT_ACCESS_FILE); } catch (Exception) { return(PAKReturnType.FAIL_UNKNOWN); } }
/* Load the contents of an existing PAK2 */ public override PAKReturnType Load() { if (!File.Exists(FilePathPAK)) { return(PAKReturnType.FAIL_TRIED_TO_LOAD_VIRTUAL_ARCHIVE); } try { //Open PAK BinaryReader ArchiveFile = new BinaryReader(File.OpenRead(FilePathPAK)); //Read the header info string MagicValidation = ""; for (int i = 0; i < 4; i++) { MagicValidation += ArchiveFile.ReadChar(); } if (MagicValidation != "PAK2") { ArchiveFile.Close(); return(PAKReturnType.FAIL_ARCHIVE_IS_NOT_EXCPETED_TYPE); } OffsetListBegin = ArchiveFile.ReadInt32() + 16; NumberOfEntries = ArchiveFile.ReadInt32(); ArchiveFile.BaseStream.Position += 4; //Skip "4" //Read all file names and create entries for (int i = 0; i < NumberOfEntries; i++) { string ThisFileName = ""; for (byte b; (b = ArchiveFile.ReadByte()) != 0x00;) { ThisFileName += (char)b; } EntryPAK2 NewPakFile = new EntryPAK2(); NewPakFile.Filename = ThisFileName; Pak2Files.Add(NewPakFile); } //Read all file offsets ArchiveFile.BaseStream.Position = OffsetListBegin; List <int> FileOffsets = new List <int>(); FileOffsets.Add(OffsetListBegin + (NumberOfEntries * 4)); for (int i = 0; i < NumberOfEntries; i++) { FileOffsets.Add(ArchiveFile.ReadInt32()); Pak2Files[i].Offset = FileOffsets[i]; } //Read in the files to entries ExtraBinaryUtils BinaryUtils = new ExtraBinaryUtils(); for (int i = 0; i < NumberOfEntries; i++) { //Must pass to RemoveLeadingNulls as each file starts with 0-3 null bytes to align files to a 4-byte block reader Pak2Files[i].Content = BinaryUtils.RemoveLeadingNulls(ArchiveFile.ReadBytes(FileOffsets[i + 1] - FileOffsets[i])); } //Close PAK ArchiveFile.Close(); return(PAKReturnType.SUCCESS); } catch (IOException) { return(PAKReturnType.FAIL_COULD_NOT_ACCESS_FILE); } catch (Exception) { return(PAKReturnType.FAIL_UNKNOWN); } }
/* Save out our PAK2 archive */ public override PAKReturnType Save() { try { //Open/create PAK2 for writing BinaryWriter ArchiveFileWrite; if (File.Exists(FilePathPAK)) { ArchiveFileWrite = new BinaryWriter(File.OpenWrite(FilePathPAK)); ArchiveFileWrite.BaseStream.SetLength(0); } else { ArchiveFileWrite = new BinaryWriter(File.Create(FilePathPAK)); } ExtraBinaryUtils BinaryUtils = new ExtraBinaryUtils(); //Write header BinaryUtils.WriteString("PAK2", ArchiveFileWrite); int OffsetListBegin_New = 0; for (int i = 0; i < Pak2Files.Count; i++) { OffsetListBegin_New += Pak2Files[i].Filename.Length + 1; } ArchiveFileWrite.Write(OffsetListBegin_New); ArchiveFileWrite.Write(Pak2Files.Count); ArchiveFileWrite.Write(4); //Write filenames for (int i = 0; i < Pak2Files.Count; i++) { BinaryUtils.WriteString(Pak2Files[i].Filename.Replace("\\", "/"), ArchiveFileWrite); ArchiveFileWrite.Write((byte)0x00); } //Write placeholder offsets for now, we'll correct them after writing the content OffsetListBegin = (int)ArchiveFileWrite.BaseStream.Position; for (int i = 0; i < Pak2Files.Count; i++) { ArchiveFileWrite.Write(0); } //Write files for (int i = 0; i < Pak2Files.Count; i++) { while (ArchiveFileWrite.BaseStream.Position % 4 != 0) { ArchiveFileWrite.Write((byte)0x00); } ArchiveFileWrite.Write(Pak2Files[i].Content); Pak2Files[i].Offset = (int)ArchiveFileWrite.BaseStream.Position; } //Re-write offsets with correct values ArchiveFileWrite.BaseStream.Position = OffsetListBegin; for (int i = 0; i < Pak2Files.Count; i++) { ArchiveFileWrite.Write(Pak2Files[i].Offset); } ArchiveFileWrite.Close(); return(PAKReturnType.SUCCESS); } catch (IOException) { return(PAKReturnType.FAIL_COULD_NOT_ACCESS_FILE); } catch (Exception) { return(PAKReturnType.FAIL_UNKNOWN); } }