diff --git a/BurnOutSharp.Builders/SGA.cs b/BurnOutSharp.Builders/SGA.cs index 476e415c..4d3457e4 100644 --- a/BurnOutSharp.Builders/SGA.cs +++ b/BurnOutSharp.Builders/SGA.cs @@ -120,8 +120,8 @@ namespace BurnOutSharp.Builders header4.MajorVersion = majorVersion; header4.MinorVersion = minorVersion; header4.FileMD5 = data.ReadBytes(0x10); - byte[] header4Name = data.ReadBytes(64); - header4.Name = Encoding.ASCII.GetString(header4Name); + byte[] header4Name = data.ReadBytes(count: 128); + header4.Name = Encoding.Unicode.GetString(header4Name).TrimEnd('\0'); header4.HeaderMD5 = data.ReadBytes(0x10); header4.HeaderLength = data.ReadUInt32(); header4.FileDataOffset = data.ReadUInt32(); @@ -137,8 +137,8 @@ namespace BurnOutSharp.Builders header6.Signature = signature; header6.MajorVersion = majorVersion; header6.MinorVersion = minorVersion; - byte[] header6Name = data.ReadBytes(64); - header6.Name = Encoding.ASCII.GetString(header6Name); + byte[] header6Name = data.ReadBytes(count: 128); + header6.Name = Encoding.Unicode.GetString(header6Name).TrimEnd('\0'); header6.HeaderLength = data.ReadUInt32(); header6.FileDataOffset = data.ReadUInt32(); header6.Dummy0 = data.ReadUInt32(); @@ -174,6 +174,9 @@ namespace BurnOutSharp.Builders #endregion + // Cache the current offset + long currentOffset = data.Position; + #region Directory Header // Try to parse the directory header @@ -196,16 +199,19 @@ namespace BurnOutSharp.Builders #region Sections // Get the sections offset - uint sectionOffset; + long sectionOffset; switch (majorVersion) { case 4: sectionOffset = (directoryHeader as DirectoryHeader4).SectionOffset; break; - case 5: sectionOffset = (directoryHeader as DirectoryHeader5).SectionOffset; break; + case 5: case 6: sectionOffset = (directoryHeader as DirectoryHeader5).SectionOffset; break; case 7: sectionOffset = (directoryHeader as DirectoryHeader7).SectionOffset; break; default: return null; } + // Adjust the sections offset based on the directory + sectionOffset += currentOffset; + // Validate the offset if (sectionOffset < 0 || sectionOffset >= data.Length) return null; @@ -218,7 +224,7 @@ namespace BurnOutSharp.Builders switch (majorVersion) { case 4: sectionCount = (directoryHeader as DirectoryHeader4).SectionCount; break; - case 5: sectionCount = (directoryHeader as DirectoryHeader5).SectionCount; break; + case 5: case 6: sectionCount = (directoryHeader as DirectoryHeader5).SectionCount; break; case 7: sectionCount = (directoryHeader as DirectoryHeader7).SectionCount; break; default: return null; @@ -229,8 +235,8 @@ namespace BurnOutSharp.Builders switch (majorVersion) { case 4: sections = new Section4[sectionCount]; break; - case 5: sections = new Section5[sectionCount]; break; - case 6: sections = new Section5[sectionCount]; break; + case 5: + case 6: case 7: sections = new Section5[sectionCount]; break; default: return null; } @@ -241,8 +247,8 @@ namespace BurnOutSharp.Builders switch (majorVersion) { case 4: sections[i] = ParseSection4(data); break; - case 5: sections[i] = ParseSection5(data); break; - case 6: sections[i] = ParseSection5(data); break; + case 5: + case 6: case 7: sections[i] = ParseSection5(data); break; default: return null; } @@ -263,7 +269,7 @@ namespace BurnOutSharp.Builders #region Folders // Get the folders offset - uint folderOffset; + long folderOffset; switch (majorVersion) { case 4: folderOffset = (directoryHeader as DirectoryHeader4).FolderOffset; break; @@ -273,6 +279,9 @@ namespace BurnOutSharp.Builders default: return null; } + // Adjust the folders offset based on the directory + folderOffset += currentOffset; + // Validate the offset if (folderOffset < 0 || folderOffset >= data.Length) return null; @@ -326,11 +335,11 @@ namespace BurnOutSharp.Builders } #endregion - + #region Files // Get the files offset - uint fileOffset; + long fileOffset; switch (majorVersion) { case 4: fileOffset = (directoryHeader as DirectoryHeader4).FileOffset; break; @@ -340,6 +349,9 @@ namespace BurnOutSharp.Builders default: return null; } + // Adjust the files offset based on the directory + fileOffset += currentOffset; + // Validate the offset if (fileOffset < 0 || fileOffset >= data.Length) return null; @@ -393,11 +405,11 @@ namespace BurnOutSharp.Builders } #endregion - + #region String Table // Get the string table offset - uint stringTableOffset; + long stringTableOffset; switch (majorVersion) { case 4: stringTableOffset = (directoryHeader as DirectoryHeader4).StringTableOffset; break; @@ -407,6 +419,9 @@ namespace BurnOutSharp.Builders default: return null; } + // Adjust the string table offset based on the directory + stringTableOffset += currentOffset; + // Validate the offset if (stringTableOffset < 0 || stringTableOffset >= data.Length) return null; @@ -425,13 +440,19 @@ namespace BurnOutSharp.Builders default: return null; } - // Create the strings array + // TODO: Are these strings actually indexed by number and not position? + // TODO: If indexed by position, I think it needs to be adjusted by start of table + + // Create the strings dictionary Dictionary strings = new Dictionary((int)stringCount); + // Get the current position to adjust the offsets + long stringTableStart = data.Position; + // Try to parse the strings for (int i = 0; i < stringCount; i++) { - long currentPosition = data.Position; + long currentPosition = data.Position - stringTableStart; strings[currentPosition] = data.ReadString(Encoding.ASCII); } @@ -445,8 +466,34 @@ namespace BurnOutSharp.Builders default: return null; } + // Loop through all folders to assign names + for (int i = 0; i < folderCount; i++) + { + switch (majorVersion) + { + case 4: (directory as Directory4).Folders[i].Name = strings[(directory as Directory4).Folders[i].NameOffset]; break; + case 5: (directory as Directory4).Folders[i].Name = strings[(directory as Directory4).Folders[i].NameOffset]; break; + case 6: (directory as Directory4).Folders[i].Name = strings[(directory as Directory4).Folders[i].NameOffset]; break; + case 7: (directory as Directory4).Folders[i].Name = strings[(directory as Directory4).Folders[i].NameOffset]; break; + default: return null; + } + } + + // Loop through all files to assign names + for (int i = 0; i < fileCount; i++) + { + switch (majorVersion) + { + case 4: (directory as Directory4).Files[i].Name = strings[(directory as Directory4).Files[i].NameOffset]; break; + case 5: (directory as Directory4).Files[i].Name = strings[(directory as Directory4).Files[i].NameOffset]; break; + case 6: (directory as Directory4).Files[i].Name = strings[(directory as Directory4).Files[i].NameOffset]; break; + case 7: (directory as Directory4).Files[i].Name = strings[(directory as Directory4).Files[i].NameOffset]; break; + default: return null; + } + } + #endregion - + return directory; } @@ -543,10 +590,10 @@ namespace BurnOutSharp.Builders { Section4 section4 = new Section4(); - byte[] section4Alias = data.ReadBytes(64); - section4.Alias = Encoding.ASCII.GetString(section4Alias); + byte[] section4Alias = data.ReadBytes(count: 64); + section4.Alias = Encoding.ASCII.GetString(section4Alias).TrimEnd('\0'); byte[] section4Name = data.ReadBytes(64); - section4.Name = Encoding.ASCII.GetString(section4Name); + section4.Name = Encoding.ASCII.GetString(section4Name).TrimEnd('\0'); section4.FolderStartIndex = data.ReadUInt16(); section4.FolderEndIndex = data.ReadUInt16(); section4.FileStartIndex = data.ReadUInt16(); @@ -566,10 +613,10 @@ namespace BurnOutSharp.Builders { Section5 section5 = new Section5(); - byte[] section5Alias = data.ReadBytes(64); - section5.Alias = Encoding.ASCII.GetString(section5Alias); + byte[] section5Alias = data.ReadBytes(count: 64); + section5.Alias = Encoding.ASCII.GetString(section5Alias).TrimEnd('\0'); byte[] section5Name = data.ReadBytes(64); - section5.Name = Encoding.ASCII.GetString(section5Name); + section5.Name = Encoding.ASCII.GetString(section5Name).TrimEnd('\0'); section5.FolderStartIndex = data.ReadUInt32(); section5.FolderEndIndex = data.ReadUInt32(); section5.FileStartIndex = data.ReadUInt32(); @@ -590,10 +637,11 @@ namespace BurnOutSharp.Builders Folder4 folder4 = new Folder4(); folder4.NameOffset = data.ReadUInt32(); - folder4.FolderStartIndex =data.ReadUInt16(); - folder4.FolderEndIndex =data.ReadUInt16(); - folder4.FileStartIndex =data.ReadUInt16(); - folder4.FileEndIndex =data.ReadUInt16(); + folder4.Name = null; // Read from string table + folder4.FolderStartIndex = data.ReadUInt16(); + folder4.FolderEndIndex = data.ReadUInt16(); + folder4.FileStartIndex = data.ReadUInt16(); + folder4.FileEndIndex = data.ReadUInt16(); return folder4; } @@ -609,6 +657,7 @@ namespace BurnOutSharp.Builders Folder5 folder5 = new Folder5(); folder5.NameOffset = data.ReadUInt32(); + folder5.Name = null; // Read from string table folder5.FolderStartIndex = data.ReadUInt32(); folder5.FolderEndIndex = data.ReadUInt32(); folder5.FileStartIndex = data.ReadUInt32(); @@ -617,7 +666,7 @@ namespace BurnOutSharp.Builders return folder5; } - /// + /// /// Parse a Stream into an SGA file version 4 /// /// Stream to parse @@ -628,6 +677,7 @@ namespace BurnOutSharp.Builders File4 file4 = new File4(); file4.NameOffset = data.ReadUInt32(); + file4.Name = null; // Read from string table file4.Offset = data.ReadUInt32(); file4.SizeOnDisk = data.ReadUInt32(); file4.Size = data.ReadUInt32(); @@ -649,6 +699,7 @@ namespace BurnOutSharp.Builders File6 file6 = new File6(); file6.NameOffset = data.ReadUInt32(); + file6.Name = null; // Read from string table file6.Offset = data.ReadUInt32(); file6.SizeOnDisk = data.ReadUInt32(); file6.Size = data.ReadUInt32(); @@ -671,6 +722,7 @@ namespace BurnOutSharp.Builders File7 file7 = new File7(); file7.NameOffset = data.ReadUInt32(); + file7.Name = null; // Read from string table file7.Offset = data.ReadUInt32(); file7.SizeOnDisk = data.ReadUInt32(); file7.Size = data.ReadUInt32(); diff --git a/BurnOutSharp.Models/SGA/File4.cs b/BurnOutSharp.Models/SGA/File4.cs index e4a3d193..a26db2e7 100644 --- a/BurnOutSharp.Models/SGA/File4.cs +++ b/BurnOutSharp.Models/SGA/File4.cs @@ -5,6 +5,8 @@ namespace BurnOutSharp.Models.SGA { public uint NameOffset; + public string Name; + public uint Offset; public uint SizeOnDisk; diff --git a/BurnOutSharp.Models/SGA/Folder.cs b/BurnOutSharp.Models/SGA/Folder.cs index 3f475e87..399b7038 100644 --- a/BurnOutSharp.Models/SGA/Folder.cs +++ b/BurnOutSharp.Models/SGA/Folder.cs @@ -5,6 +5,8 @@ namespace BurnOutSharp.Models.SGA { public uint NameOffset; + public string Name; + public T FolderStartIndex; public T FolderEndIndex; diff --git a/BurnOutSharp.Wrappers/SGA.cs b/BurnOutSharp.Wrappers/SGA.cs new file mode 100644 index 00000000..0e2910e2 --- /dev/null +++ b/BurnOutSharp.Wrappers/SGA.cs @@ -0,0 +1,661 @@ +using System; +using System.Collections.Generic; +using System.IO; + +namespace BurnOutSharp.Wrappers +{ + public class SGA : WrapperBase + { + #region Pass-Through Properties + + #region Header + + /// + public string Signature => _file.Header.Signature; + + /// + public ushort MajorVersion => _file.Header.MajorVersion; + + /// + public ushort MinorVersion => _file.Header.MinorVersion; + + /// + public byte[] FileMD5 + { + get + { + switch (MajorVersion) + { + case 4: return (_file.Header as Models.SGA.Header4).FileMD5; + case 5: return (_file.Header as Models.SGA.Header4).FileMD5; + default: return null; + }; + } + } + + /// + public string Name + { + get + { + switch (MajorVersion) + { + case 4: return (_file.Header as Models.SGA.Header4).Name; + case 5: return (_file.Header as Models.SGA.Header4).Name; + case 6: return (_file.Header as Models.SGA.Header6).Name; + case 7: return (_file.Header as Models.SGA.Header6).Name; + default: return null; + }; + } + } + + /// + public byte[] HeaderMD5 + { + get + { + switch (MajorVersion) + { + case 4: return (_file.Header as Models.SGA.Header4).HeaderMD5; + case 5: return (_file.Header as Models.SGA.Header4).HeaderMD5; + default: return null; + }; + } + } + + /// + public uint? HeaderLength + { + get + { + switch (MajorVersion) + { + case 4: return (_file.Header as Models.SGA.Header4).HeaderLength; + case 5: return (_file.Header as Models.SGA.Header4).HeaderLength; + case 6: return (_file.Header as Models.SGA.Header6).HeaderLength; + case 7: return (_file.Header as Models.SGA.Header6).HeaderLength; + default: return null; + }; + } + } + + /// + public uint? FileDataOffset + { + get + { + switch (MajorVersion) + { + case 4: return (_file.Header as Models.SGA.Header4).FileDataOffset; + case 5: return (_file.Header as Models.SGA.Header4).FileDataOffset; + case 6: return (_file.Header as Models.SGA.Header6).FileDataOffset; + case 7: return (_file.Header as Models.SGA.Header6).FileDataOffset; + default: return null; + }; + } + } + + /// + public uint? Dummy0 + { + get + { + switch (MajorVersion) + { + case 4: return (_file.Header as Models.SGA.Header4).Dummy0; + case 5: return (_file.Header as Models.SGA.Header4).Dummy0; + case 6: return (_file.Header as Models.SGA.Header6).Dummy0; + case 7: return (_file.Header as Models.SGA.Header6).Dummy0; + default: return null; + }; + } + } + + #endregion + + #region Directory + + #region Directory Header + + /// + public uint? SectionOffset + { + get + { + switch (MajorVersion) + { + case 4: return (_file.Directory as Models.SGA.Directory4).DirectoryHeader.SectionOffset; + case 5: return (_file.Directory as Models.SGA.Directory5).DirectoryHeader.SectionOffset; + case 6: return (_file.Directory as Models.SGA.Directory6).DirectoryHeader.SectionOffset; + case 7: return (_file.Directory as Models.SGA.Directory7).DirectoryHeader.SectionOffset; + default: return null; + }; + } + } + + /// + public uint? SectionCount + { + get + { + switch (MajorVersion) + { + case 4: return (_file.Directory as Models.SGA.Directory4).DirectoryHeader.SectionCount; + case 5: return (_file.Directory as Models.SGA.Directory5).DirectoryHeader.SectionCount; + case 6: return (_file.Directory as Models.SGA.Directory6).DirectoryHeader.SectionCount; + case 7: return (_file.Directory as Models.SGA.Directory7).DirectoryHeader.SectionCount; + default: return null; + }; + } + } + + /// + public uint? FolderOffset + { + get + { + switch (MajorVersion) + { + case 4: return (_file.Directory as Models.SGA.Directory4).DirectoryHeader.FolderOffset; + case 5: return (_file.Directory as Models.SGA.Directory5).DirectoryHeader.FolderOffset; + case 6: return (_file.Directory as Models.SGA.Directory6).DirectoryHeader.FolderOffset; + case 7: return (_file.Directory as Models.SGA.Directory7).DirectoryHeader.FolderOffset; + default: return null; + }; + } + } + + /// + public uint? FolderCount + { + get + { + switch (MajorVersion) + { + case 4: return (_file.Directory as Models.SGA.Directory4).DirectoryHeader.FolderCount; + case 5: return (_file.Directory as Models.SGA.Directory5).DirectoryHeader.FolderCount; + case 6: return (_file.Directory as Models.SGA.Directory6).DirectoryHeader.FolderCount; + case 7: return (_file.Directory as Models.SGA.Directory7).DirectoryHeader.FolderCount; + default: return null; + }; + } + } + + /// + public uint? FileOffset + { + get + { + switch (MajorVersion) + { + case 4: return (_file.Directory as Models.SGA.Directory4).DirectoryHeader.FileOffset; + case 5: return (_file.Directory as Models.SGA.Directory5).DirectoryHeader.FileOffset; + case 6: return (_file.Directory as Models.SGA.Directory6).DirectoryHeader.FileOffset; + case 7: return (_file.Directory as Models.SGA.Directory7).DirectoryHeader.FileOffset; + default: return null; + }; + } + } + + /// + public uint? FileCount + { + get + { + switch (MajorVersion) + { + case 4: return (_file.Directory as Models.SGA.Directory4).DirectoryHeader.FileCount; + case 5: return (_file.Directory as Models.SGA.Directory5).DirectoryHeader.FileCount; + case 6: return (_file.Directory as Models.SGA.Directory6).DirectoryHeader.FileCount; + case 7: return (_file.Directory as Models.SGA.Directory7).DirectoryHeader.FileCount; + default: return null; + }; + } + } + + /// + public uint? StringTableOffset + { + get + { + switch (MajorVersion) + { + case 4: return (_file.Directory as Models.SGA.Directory4).DirectoryHeader.StringTableOffset; + case 5: return (_file.Directory as Models.SGA.Directory5).DirectoryHeader.StringTableOffset; + case 6: return (_file.Directory as Models.SGA.Directory6).DirectoryHeader.StringTableOffset; + case 7: return (_file.Directory as Models.SGA.Directory7).DirectoryHeader.StringTableOffset; + default: return null; + }; + } + } + + /// + public uint? StringTableCount + { + get + { + switch (MajorVersion) + { + case 4: return (_file.Directory as Models.SGA.Directory4).DirectoryHeader.StringTableCount; + case 5: return (_file.Directory as Models.SGA.Directory5).DirectoryHeader.StringTableCount; + case 6: return (_file.Directory as Models.SGA.Directory6).DirectoryHeader.StringTableCount; + case 7: return (_file.Directory as Models.SGA.Directory7).DirectoryHeader.StringTableCount; + default: return null; + }; + } + } + + /// + public uint? HashTableOffset + { + get + { + switch (MajorVersion) + { + case 7: return (_file.Directory as Models.SGA.Directory7).DirectoryHeader.HashTableOffset; + default: return null; + }; + } + } + + /// + public uint? BlockSize + { + get + { + switch (MajorVersion) + { + case 7: return (_file.Directory as Models.SGA.Directory7).DirectoryHeader.BlockSize; + default: return null; + }; + } + } + + #endregion + + #region Sections + + /// + public object[] Sections + { + get + { + switch (MajorVersion) + { + case 4: return (_file.Directory as Models.SGA.Directory4).Sections; + case 5: return (_file.Directory as Models.SGA.Directory5).Sections; + case 6: return (_file.Directory as Models.SGA.Directory6).Sections; + case 7: return (_file.Directory as Models.SGA.Directory7).Sections; + default: return null; + }; + } + } + + #endregion + + #region Folders + + /// + public object[] Folders + { + get + { + switch (MajorVersion) + { + case 4: return (_file.Directory as Models.SGA.Directory4).Folders; + case 5: return (_file.Directory as Models.SGA.Directory5).Folders; + case 6: return (_file.Directory as Models.SGA.Directory6).Folders; + case 7: return (_file.Directory as Models.SGA.Directory7).Folders; + default: return null; + }; + } + } + + #endregion + + #region Files + + /// + public object[] Files + { + get + { + switch (MajorVersion) + { + case 4: return (_file.Directory as Models.SGA.Directory4).Files; + case 5: return (_file.Directory as Models.SGA.Directory5).Files; + case 6: return (_file.Directory as Models.SGA.Directory6).Files; + case 7: return (_file.Directory as Models.SGA.Directory7).Files; + default: return null; + }; + } + } + + #endregion + + // TODO: Figure out how to deal with all of the parts of the directory + // TODO: Should anything be passed through? + + #endregion + + #endregion + + #region Extension Properties + + // TODO: Figure out what extension oroperties are needed + + #endregion + + #region Instance Variables + + /// + /// Internal representation of the SGA + /// + private Models.SGA.File _file; + + #endregion + + #region Constructors + + /// + /// Private constructor + /// + private SGA() { } + + /// + /// Create an SGA from a byte array and offset + /// + /// Byte array representing the SGA + /// Offset within the array to parse + /// An SGA wrapper on success, null on failure + public static SGA Create(byte[] data, int offset) + { + // If the data is invalid + if (data == null) + return null; + + // If the offset is out of bounds + if (offset < 0 || offset >= data.Length) + return null; + + // Create a memory stream and use that + MemoryStream dataStream = new MemoryStream(data, offset, data.Length - offset); + return Create(dataStream); + } + + /// + /// Create a SGA from a Stream + /// + /// Stream representing the SGA + /// An SGA wrapper on success, null on failure + public static SGA Create(Stream data) + { + // If the data is invalid + if (data == null || data.Length == 0 || !data.CanSeek || !data.CanRead) + return null; + + var file = Builders.SGA.ParseFile(data); + if (file == null) + return null; + + var wrapper = new SGA + { + _file = file, + _dataSource = DataSource.Stream, + _streamData = data, + }; + return wrapper; + } + + #endregion + + #region Printing + + /// + public override void Print() + { + Console.WriteLine("SGA Information:"); + Console.WriteLine("-------------------------"); + Console.WriteLine(); + + // Header + PrintHeader(); + + // Directory + PrintDirectoryHeader(); + PrintSections(); + PrintFolders(); + PrintFiles(); + // TODO: Should we print the string table? + } + + /// + /// Print header information + /// + private void PrintHeader() + { + Console.WriteLine(" Header Information:"); + Console.WriteLine(" -------------------------"); + Console.WriteLine($" Signature: {Signature}"); + Console.WriteLine($" Major version: {MajorVersion}"); + Console.WriteLine($" Minor version: {MinorVersion}"); + Console.WriteLine($" File MD5: {(FileMD5 == null ? "[NULL]" : BitConverter.ToString(FileMD5).Replace("-", string.Empty))}"); + Console.WriteLine($" Name: {Name ?? "[NULL]"}"); + Console.WriteLine($" Header MD5: {(HeaderMD5 == null ? "[NULL]" : BitConverter.ToString(HeaderMD5).Replace("-", string.Empty))}"); + Console.WriteLine($" Header length: {HeaderLength.ToString() ?? "[NULL]"}"); + Console.WriteLine($" File data offset: {FileDataOffset.ToString() ?? "[NULL]"}"); + Console.WriteLine($" Dummy 0: {Dummy0.ToString() ?? "[NULL]"}"); + Console.WriteLine(); + } + + /// + /// Print directory header information + /// + private void PrintDirectoryHeader() + { + Console.WriteLine(" Directory Header Information:"); + Console.WriteLine(" -------------------------"); + Console.WriteLine($" Section offset: {SectionOffset.ToString() ?? "[NULL]"}"); + Console.WriteLine($" Section count: {SectionCount.ToString() ?? "[NULL]"}"); + Console.WriteLine($" Folder offset: {FolderOffset.ToString() ?? "[NULL]"}"); + Console.WriteLine($" Folder count: {FolderCount.ToString() ?? "[NULL]"}"); + Console.WriteLine($" File offset: {FileOffset.ToString() ?? "[NULL]"}"); + Console.WriteLine($" File count: {FileCount.ToString() ?? "[NULL]"}"); + Console.WriteLine($" String table offset: {StringTableOffset.ToString() ?? "[NULL]"}"); + Console.WriteLine($" String table count: {StringTableCount.ToString() ?? "[NULL]"}"); + Console.WriteLine($" Hash table offset: {HashTableOffset?.ToString() ?? "[NULL]"}"); + Console.WriteLine($" Block size: {BlockSize?.ToString() ?? "[NULL]"}"); + Console.WriteLine(); + } + + /// + /// Print sections information + /// + private void PrintSections() + { + Console.WriteLine(" Sections Information:"); + Console.WriteLine(" -------------------------"); + if (Sections == null || Sections.Length == 0) + { + Console.WriteLine(" No sections"); + } + else + { + for (int i = 0; i < Sections.Length; i++) + { + Console.WriteLine($" Section {i}"); + switch (MajorVersion) + { + case 4: + var section4 = Sections[i] as Models.SGA.Section4; + Console.WriteLine($" Alias: {section4.Alias ?? "[NULL]"}"); + Console.WriteLine($" Name: {section4.Name ?? "[NULL]"}"); + Console.WriteLine($" Folder start index: {section4.FolderStartIndex}"); + Console.WriteLine($" Folder end index: {section4.FolderEndIndex}"); + Console.WriteLine($" File start index: {section4.FileStartIndex}"); + Console.WriteLine($" File end index: {section4.FileEndIndex}"); + Console.WriteLine($" Folder root index: {section4.FolderRootIndex}"); + break; + + case 5: + case 6: + case 7: + var section5 = Sections[i] as Models.SGA.Section5; + Console.WriteLine($" Alias: {section5.Alias ?? "[NULL]"}"); + Console.WriteLine($" Name: {section5.Name ?? "[NULL]"}"); + Console.WriteLine($" Folder start index: {section5.FolderStartIndex}"); + Console.WriteLine($" Folder end index: {section5.FolderEndIndex}"); + Console.WriteLine($" File start index: {section5.FileStartIndex}"); + Console.WriteLine($" File end index: {section5.FileEndIndex}"); + Console.WriteLine($" Folder root index: {section5.FolderRootIndex}"); + break; + default: + Console.WriteLine($" Unknown format for version {MajorVersion}"); + break; + } + } + } + Console.WriteLine(); + } + + /// + /// Print folders information + /// + private void PrintFolders() + { + Console.WriteLine(" Folders Information:"); + Console.WriteLine(" -------------------------"); + if (Folders == null || Folders.Length == 0) + { + Console.WriteLine(" No folders"); + } + else + { + for (int i = 0; i < Folders.Length; i++) + { + Console.WriteLine($" Folder {i}"); + switch (MajorVersion) + { + case 4: + var folder4 = Folders[i] as Models.SGA.Folder4; + Console.WriteLine($" Name offset: {folder4.NameOffset}"); + Console.WriteLine($" Name: {folder4.Name ?? "[NULL]"}"); + Console.WriteLine($" Folder start index: {folder4.FolderStartIndex}"); + Console.WriteLine($" Folder end index: {folder4.FolderEndIndex}"); + Console.WriteLine($" File start index: {folder4.FileStartIndex}"); + Console.WriteLine($" File end index: {folder4.FileEndIndex}"); + break; + + case 5: + case 6: + case 7: + var folder5 = Folders[i] as Models.SGA.Folder5; + Console.WriteLine($" Name offset: {folder5.NameOffset}"); + Console.WriteLine($" Name: {folder5.Name ?? "[NULL]"}"); + Console.WriteLine($" Folder start index: {folder5.FolderStartIndex}"); + Console.WriteLine($" Folder end index: {folder5.FolderEndIndex}"); + Console.WriteLine($" File start index: {folder5.FileStartIndex}"); + Console.WriteLine($" File end index: {folder5.FileEndIndex}"); + break; + default: + Console.WriteLine($" Unknown format for version {MajorVersion}"); + break; + } + } + } + Console.WriteLine(); + } + + /// + /// Print files information + /// + private void PrintFiles() + { + Console.WriteLine(" Files Information:"); + Console.WriteLine(" -------------------------"); + if (Files == null || Files.Length == 0) + { + Console.WriteLine(" No files"); + } + else + { + for (int i = 0; i < Files.Length; i++) + { + Console.WriteLine($" File {i}"); + switch (MajorVersion) + { + case 4: + case 5: + var file4 = Files[i] as Models.SGA.File4; + Console.WriteLine($" Name offset: {file4.NameOffset}"); + Console.WriteLine($" Name: {file4.Name ?? "[NULL]"}"); + Console.WriteLine($" Offset: {file4.Offset}"); + Console.WriteLine($" Size on disk: {file4.SizeOnDisk}"); + Console.WriteLine($" Size: {file4.Size}"); + Console.WriteLine($" Time modified: {file4.TimeModified}"); + Console.WriteLine($" Dummy 0: {file4.Dummy0}"); + Console.WriteLine($" Type: {file4.Type}"); + break; + + case 6: + var file6 = Files[i] as Models.SGA.File6; + Console.WriteLine($" Name offset: {file6.NameOffset}"); + Console.WriteLine($" Name: {file6.Name ?? "[NULL]"}"); + Console.WriteLine($" Offset: {file6.Offset}"); + Console.WriteLine($" Size on disk: {file6.SizeOnDisk}"); + Console.WriteLine($" Size: {file6.Size}"); + Console.WriteLine($" Time modified: {file6.TimeModified}"); + Console.WriteLine($" Dummy 0: {file6.Dummy0}"); + Console.WriteLine($" Type: {file6.Type}"); + Console.WriteLine($" CRC32: {file6.CRC32}"); + break; + case 7: + var file7 = Files[i] as Models.SGA.File7; + Console.WriteLine($" Name offset: {file7.NameOffset}"); + Console.WriteLine($" Name: {file7.Name ?? "[NULL]"}"); + Console.WriteLine($" Offset: {file7.Offset}"); + Console.WriteLine($" Size on disk: {file7.SizeOnDisk}"); + Console.WriteLine($" Size: {file7.Size}"); + Console.WriteLine($" Time modified: {file7.TimeModified}"); + Console.WriteLine($" Dummy 0: {file7.Dummy0}"); + Console.WriteLine($" Type: {file7.Type}"); + Console.WriteLine($" CRC32: {file7.CRC32}"); + Console.WriteLine($" Hash offset: {file7.HashOffset}"); + break; + default: + Console.WriteLine($" Unknown format for version {MajorVersion}"); + break; + } + } + } + Console.WriteLine(); + } + + #endregion + + #region Extraction + + /// + /// Extract all files from the SGA to an output directory + /// + /// Output directory to write to + /// True if all files extracted, false otherwise + public bool ExtractAll(string outputDirectory) + { + return false; + } + + /// + /// Extract a file from the SGA to an output directory by index + /// + /// File index to extract + /// Output directory to write to + /// True if the file extracted, false otherwise + public bool ExtractFile(int index, string outputDirectory) + { + return false; + } + + #endregion + } +} \ No newline at end of file diff --git a/Test/Program.cs b/Test/Program.cs index f648f5e6..6e75c120 100644 --- a/Test/Program.cs +++ b/Test/Program.cs @@ -207,6 +207,8 @@ namespace Test /// private static void PrintFileInfo(string file) { + Console.WriteLine($"Attempting to print info for {file}"); + using (Stream stream = File.Open(file, FileMode.Open, FileAccess.Read, FileShare.Read)) { // Read the first 8 bytes @@ -422,6 +424,25 @@ namespace Test pak.Print(); } + // PAK + else if (ft == SupportedFileType.SGA) + { + // Build the archive information + Console.WriteLine("Creating SGA deserializer"); + Console.WriteLine(); + + var sga = SGA.Create(stream); + if (sga == null) + { + Console.WriteLine("Something went wrong parsing SGA"); + Console.WriteLine(); + return; + } + + // Print the SGA info to screen + sga.Print(); + } + // VBSP else if (ft == SupportedFileType.VBSP) {