public void ParseTable(UTF_File table, string tableName, Version version, bool throwExIfNewColumn) { if (Name != tableName) { throw new InvalidOperationException("AcbFormatHelperTable.ParseTable: table name does not match!"); } //Check if they exist foreach (var utfColumn in table.Columns) { if (utfColumn.Name == "MusicPackageType" && utfColumn.TypeFlag == TypeFlag.Int32) { continue; //Is MusicPackage, a column added by ACE. Skip this. } var column = GetColumn(utfColumn.Name, utfColumn.TypeFlag, throwExIfNewColumn, table.Columns.IndexOf(utfColumn)); column.SetExists(version); } //Check if they dont exist foreach (var column in Columns) { var utfColumn = table.GetColumn(column.Name); if (utfColumn != null) { if (utfColumn.TypeFlag == column.ValueType) { continue; } } //Doesn't exist column.SetDoesNotExist(version); } }
public AWB_CPK(AFS2_File afs2File, UTF_File cpkHeader) { CPK_Header = cpkHeader; foreach (var entry in afs2File.Entries.OrderBy(x => x.AwbId)) { Entries.Add(new CPK_Entry(entry.AwbId, entry.bytes)); } }
public UTF_File CreateTable(Version version, string name) { UTF_File table = new UTF_File(name); foreach (var column in Columns) { if (column.DoesExist(version)) { table.Columns.Add(new UTF_Column(column.Name, column.ValueType)); } } return(table); }
public void ParseFiles(string[] filePaths) { foreach (var file in filePaths) { #if !DEBUG try { #endif if (Path.GetExtension(file).ToLower() == ".acb") { Console.WriteLine($"Parsing \"{file}\"..."); byte[] bytes = File.ReadAllBytes(file); UTF_File utfFile = UTF_File.LoadUtfTable(bytes, bytes.Length); AcbFormatHelperMain.ParseFile(utfFile, false); } #if !DEBUG } catch { Console.WriteLine($"\"{file}\" failed, skipping..."); } #endif } }
public void ParseFile(UTF_File utfFile) { AcbFormatHelperMain.ParseFile(utfFile, true); }
public void ParseFile(UTF_File acbFile, bool throwExIfNewColumn) { Version version = BigEndianConverter.UIntToVersion(acbFile.GetValue <uint>("Version", TypeFlag.UInt32, 0), true); AddVersion(version.ToString()); //Parse tables foreach (var column in acbFile.Columns) { #if !DEBUG if (ExcludedTables.Contains(column.Name)) { continue; } #endif var tableColumn = (column.Rows[0].UtfTable != null) ? column.Rows[0].UtfTable : column.UtfTable; if (tableColumn != null) { var table = GetTable(column.Name, throwExIfNewColumn); table.SetExists(version); table.ParseTable(tableColumn, column.Name, version, throwExIfNewColumn); } } //Check if they dont exist foreach (var table in Tables) { #if !DEBUG if (ExcludedTables.Contains(table.Name)) { continue; } #endif var utfColumn = acbFile.GetColumn(table.Name); if (utfColumn != null) { byte[] columnData = (utfColumn.StorageFlag == StorageFlag.PerRow) ? utfColumn.Rows[0].Bytes : utfColumn.Bytes; UTF_File columnTable = (utfColumn.StorageFlag == StorageFlag.PerRow) ? utfColumn.Rows[0].UtfTable : utfColumn.UtfTable; if (columnTable == null && columnData == null) { continue; //Table does not exist, but there is also no other data... skip } if (columnTable != null) { continue; //Table exists } //Handle StreamAwbHash table. This can be a UtfTable or a Byte array depending on ACB version, or if a stream AWB is present (can be null 16 bytes if there is no external AWB, even on newer versions with UtfTable) if (columnTable == null && columnData != null && table.Name == "StreamAwbHash") { bool valid = true; foreach (var _byte in columnData) { if (_byte != 0) { valid = false; } } if (valid) { continue; } } } //Doesn't exist table.SetDoesNotExist(version); } //Parse all header columns Header.ParseTable(acbFile, "Header", version, throwExIfNewColumn); //Sort columns Sort(); }
public static AWB_CPK Load(byte[] bytes, int offset, int length) { AWB_CPK cpk = new AWB_CPK(); using (MemoryStream stream = new MemoryStream(bytes, offset, length)) { //CPK Header if (stream.ReadInt32() != CPK_SIGNATURE) { throw new InvalidDataException("CPK Signature not found."); } cpk.CPK_Unk1 = stream.ReadInt32(); ulong cpkTableSize = stream.ReadUInt32(); stream.Seek(4, SeekOrigin.Current); cpk.CPK_Header = UTF_File.LoadUtfTable(stream.ReadBytes((int)cpkTableSize), (int)cpkTableSize); //ITOC Header if (cpk.CPK_Header.ColumnHasValue("ItocOffset")) { long itocOffset = (long)cpk.CPK_Header.GetValue <ulong>("ItocOffset", TypeFlag.UInt64, 0); if (itocOffset > 0) { stream.Seek(itocOffset, SeekOrigin.Begin); if (stream.ReadInt32() != ITOC_SIGNATURE) { throw new InvalidDataException("ITOC Signature not found."); } cpk.ITOC_Unk1 = stream.ReadInt32(); ulong itocTableSize = stream.ReadUInt32(); stream.Seek(4, SeekOrigin.Current); cpk.ITOC = UTF_File.LoadUtfTable(stream.ReadBytes((int)itocTableSize), (int)itocTableSize); if (cpk.ITOC.TableName != "CpkItocInfo") { throw new InvalidDataException($"Unexpected ITOC: {cpk.ITOC.TableName}. Cannot continue."); } } } //TOC if (cpk.CPK_Header.ColumnHasValue("TocOffset")) { if (cpk.CPK_Header.GetValue <ulong>("TocOffset", TypeFlag.UInt64, 0) != 0) { throw new InvalidDataException($"This cpk has a TOC table, cannot continue."); } } //ETOC if (cpk.CPK_Header.ColumnHasValue("EtocOffset")) { if (cpk.CPK_Header.GetValue <ulong>("EtocOffset", TypeFlag.UInt64, 0) != 0) { throw new InvalidDataException($"This cpk has a ETOC table, cannot continue."); } } //Validate header if (!cpk.CPK_Header.ColumnHasValue("Files")) { throw new InvalidDataException("\"Files\" column not found or missing value."); } if (!cpk.CPK_Header.ColumnHasValue("Align")) { throw new InvalidDataException("\"Align\" column not found or missing value."); } if (!cpk.CPK_Header.ColumnHasValue("ContentOffset")) { throw new InvalidDataException("\"ContentOffset\" column not found or missing value."); } uint numFiles = cpk.CPK_Header.GetValue <uint>("Files", TypeFlag.UInt32, 0); ulong contentOffset = cpk.CPK_Header.GetValue <ulong>("ContentOffset", TypeFlag.UInt64, 0); ushort align = cpk.CPK_Header.GetValue <ushort>("Align", TypeFlag.UInt16, 0); ulong currentOffset = contentOffset; //Validate ITOC if (!cpk.ITOC.ColumnHasValue("FilesL") || !cpk.ITOC.ColumnHasValue("FilesH")) { throw new InvalidDataException($"\"FilesL\" or \"FilesH\" column not found or missing value."); } int numH = (int)cpk.ITOC.GetValue <uint>("FilesH", TypeFlag.UInt32, 0); int numL = (int)cpk.ITOC.GetValue <uint>("FilesL", TypeFlag.UInt32, 0); UTF_File dataH = cpk.ITOC.GetColumnTable("DataH", true); UTF_File dataL = cpk.ITOC.GetColumnTable("DataL", true); if (numH + numL != numFiles) { throw new InvalidDataException($"FilesH + FilesL does not equal Files. Cannot continue."); } //Load files for (ushort id = 0; id < numFiles; id++) { uint fileSize = 0; uint extractSize = 0; bool found = false; //DataL seems to be for files of less than 65535 bytes. for (int i = 0; i < numL; i++) { if (dataL.GetValue <ushort>("ID", TypeFlag.UInt16, i) != id) { continue; } fileSize = dataL.GetValue <ushort>("FileSize", TypeFlag.UInt16, i); extractSize = dataL.GetValue <ushort>("ExtractSize", TypeFlag.UInt16, i); found = true; break; } if (!found) { for (int i = 0; i < numH; i++) { if (dataH.GetValue <ushort>("ID", TypeFlag.UInt16, i) != id) { continue; } fileSize = dataH.GetValue <uint>("FileSize", TypeFlag.UInt32, i); extractSize = dataH.GetValue <uint>("ExtractSize", TypeFlag.UInt32, i); found = true; break; } } if (found) { if (fileSize == 0) { continue; //null entry } stream.Seek((long)currentOffset, SeekOrigin.Begin); byte[] data = stream.ReadBytes((int)fileSize); if (data.Length > 8) { string isComp = Encoding.ASCII.GetString(data.GetRange(0, 8)); if (isComp == "CRILAYLA") { int size = (int)((extractSize > fileSize) ? extractSize : fileSize); data = _cpk.DecompressCRILAYLA(data, size); } } cpk.Entries.Add(new CPK_Entry(id, data)); //Increment offset currentOffset += fileSize; if ((currentOffset % align) != 0) { currentOffset += (align - (currentOffset % align)); } } else { throw new Exception($"Could not find the file for ID {id} in either \"DataL\" or \"DataH\"."); } } } return(cpk); }
public byte[] Write() { PadWithNullEntries(); ushort align = CPK_Header.GetValue <ushort>("Align", TypeFlag.UInt16, 0); List <byte> bytes = new List <byte>(); List <byte> contentBytes = new List <byte>(); //Update header ulong contentOffset = 0x800; ulong contentSize = GetContentSize(); CPK_Header.SetValue("Files", (uint)Entries.Count, TypeFlag.UInt32, 0); CPK_Header.SetValue("ContentOffset", contentOffset, TypeFlag.UInt64, 0); CPK_Header.SetValue("ContentSize", contentSize, TypeFlag.UInt64, 0); CPK_Header.SetValue("ItocOffset", contentOffset + contentSize, TypeFlag.UInt64, 0); //ITOC int numH = 0; int numL = 0; //Create DataL/DataH tables UTF_File dataH = new UTF_File("CpkItocH"); UTF_File dataL = new UTF_File("CpkItocL"); dataL.Columns.Add(new UTF_Column("ID", TypeFlag.UInt16, StorageFlag.PerRow)); dataL.Columns.Add(new UTF_Column("FileSize", TypeFlag.UInt16, StorageFlag.PerRow)); dataL.Columns.Add(new UTF_Column("ExtractSize", TypeFlag.UInt16, StorageFlag.PerRow)); dataH.Columns.Add(new UTF_Column("ID", TypeFlag.UInt16, StorageFlag.PerRow)); dataH.Columns.Add(new UTF_Column("FileSize", TypeFlag.UInt32, StorageFlag.PerRow)); dataH.Columns.Add(new UTF_Column("ExtractSize", TypeFlag.UInt32, StorageFlag.PerRow)); foreach (var entry in Entries) { if (entry.Data.Length > ushort.MaxValue) { dataH.AddValue("FileSize", TypeFlag.UInt32, numH, entry.Data.Length.ToString()); dataH.AddValue("ExtractSize", TypeFlag.UInt32, numH, entry.Data.Length.ToString()); dataH.AddValue("ID", TypeFlag.UInt16, numH, entry.ID.ToString()); numH++; } else { dataL.AddValue("FileSize", TypeFlag.UInt16, numL, entry.Data.Length.ToString()); dataL.AddValue("ExtractSize", TypeFlag.UInt16, numL, entry.Data.Length.ToString()); dataL.AddValue("ID", TypeFlag.UInt16, numL, entry.ID.ToString()); numL++; } //Write data if (entry.Data.Length > 0) { contentBytes.AddRange(entry.Data); if ((contentBytes.Count % align) != 0) { contentBytes.AddRange(new byte[(align - (contentBytes.Count % align))]); } } } //Create ITOC table ITOC = new UTF_File(); ITOC.TableName = "CpkItocInfo"; ITOC.Columns.Add(new UTF_Column("FilesL", TypeFlag.UInt32, StorageFlag.PerRow)); ITOC.Columns.Add(new UTF_Column("FilesH", TypeFlag.UInt32, StorageFlag.PerRow)); ITOC.Columns.Add(new UTF_Column("DataL", TypeFlag.Data, StorageFlag.PerRow)); ITOC.Columns.Add(new UTF_Column("DataH", TypeFlag.Data, StorageFlag.PerRow)); ITOC.AddValue("FilesL", TypeFlag.UInt32, 0, numL.ToString()); ITOC.AddValue("FilesH", TypeFlag.UInt32, 0, numH.ToString()); ITOC.AddData("DataL", 0, dataL.Write()); ITOC.AddData("DataH", 0, dataH.Write()); //Create file byte[] itocBytes = ITOC.Write(); CPK_Header.SetValue("ItocSize", itocBytes.Length, TypeFlag.UInt64, 0); byte[] headerBytes = CPK_Header.Write(); bytes.AddRange(BitConverter.GetBytes(CPK_SIGNATURE)); bytes.AddRange(BitConverter.GetBytes(CPK_Unk1)); bytes.AddRange(BitConverter.GetBytes((ulong)headerBytes.Length)); bytes.AddRange(headerBytes); bytes.AddRange(new byte[2040 - bytes.Count]); bytes.AddRange(BitConverter.GetBytes((ulong)CRI_SIGNATURE)); if (bytes.Count != 0x800) { throw new InvalidDataException("CPK header is wrong size."); } bytes.AddRange(contentBytes); bytes.AddRange(BitConverter.GetBytes(ITOC_SIGNATURE)); bytes.AddRange(BitConverter.GetBytes(ITOC_Unk1)); bytes.AddRange(BitConverter.GetBytes((ulong)itocBytes.Length)); bytes.AddRange(itocBytes); return(bytes.ToArray()); }