Questo sito utilizza cookies solo per scopi di autenticazione sul sito e nient'altro. Nessuna informazione personale viene tracciata. Leggi l'informativa sui cookies.
Username: Password: oppure
BSA Prospector - BsaFile.vb

BsaFile.vb

Caricato da: Totem
Scarica il programma completo

  1. Imports ManagedZLib
  2.  
  3. Public Enum BsaArchiveFlags As UInt32
  4.     HasDirectoryNames = 1
  5.     HasFileNames = 2
  6.     CompressionEnabled = 4
  7.     'Gli altri campi sono sconosciuti
  8. End Enum
  9.  
  10. Public Enum BsaFileTypeFlags As UInt32
  11.     Nif = 1
  12.     Dds = 2
  13.     Xml = 4
  14.     Wav = 8
  15.     Mp3 = 16
  16.     TxtHtmlBatScc = 32
  17.     Spt = 64
  18.     TexFnt = 128
  19.     Ctl = 256
  20. End Enum
  21.  
  22. Public Class BsaArchive
  23.  
  24.     Private Structure FolderRecord
  25.         Public NameHash As UInt64
  26.         Public Count As UInt32
  27.         Public Offset As UInt32
  28.     End Structure
  29.  
  30.     Private Structure FileRecord
  31.         Public NameHash As UInt64
  32.         Public Size As UInt32
  33.         Public Offset As UInt32
  34.     End Structure
  35.  
  36.     Private Structure FileRecordBlock
  37.         Public FolderName As String
  38.         Public Records() As FileRecord
  39.     End Structure
  40.  
  41.     Public Structure BsaFile
  42.         Private _Name As String
  43.         Private _Offset, _Size As UInt32
  44.  
  45.         Public ReadOnly Property Name() As String
  46.             Get
  47.                 Return _Name
  48.             End Get
  49.         End Property
  50.  
  51.         Public ReadOnly Property Offset() As UInt32
  52.             Get
  53.                 Return _Offset
  54.             End Get
  55.         End Property
  56.  
  57.         Public Property Size() As UInt32
  58.             Get
  59.                 Return _Size
  60.             End Get
  61.             Set(ByVal value As UInt32)
  62.                 _Size = value
  63.             End Set
  64.         End Property
  65.  
  66.         Sub New(ByVal Name As String, ByVal Offset As UInt32, ByVal Size As UInt32)
  67.             _Name = Name
  68.             _Offset = Offset
  69.             _Size = Size
  70.         End Sub
  71.     End Structure
  72.  
  73.     Public Structure BsaFolder
  74.         Private _Name As String
  75.         Private _Files As List(Of BsaFile)
  76.  
  77.         Public ReadOnly Property Name() As String
  78.             Get
  79.                 Return _Name
  80.             End Get
  81.         End Property
  82.  
  83.         Public ReadOnly Property Files() As List(Of BsaFile)
  84.             Get
  85.                 Return _Files
  86.             End Get
  87.         End Property
  88.  
  89.         Sub New(ByVal Name As String)
  90.             _Name = Name
  91.             _Files = New List(Of BsaFile)
  92.         End Sub
  93.     End Structure
  94.  
  95.     Private _FileName As String
  96.     Private _ArchiveFlags As BsaArchiveFlags
  97.     Private _FileTypeFlags As BsaFileTypeFlags
  98.     Private _FoldersCount, _FilesCount, _Version As UInt32
  99.     Private _Folders As List(Of BsaFolder)
  100.  
  101.     Private Stream As IO.FileStream
  102.     Private TotalFolderNamesLength, TotalFileNamesLength As UInt32
  103.  
  104.     Public ReadOnly Property FileName() As String
  105.         Get
  106.             Return _FileName
  107.         End Get
  108.     End Property
  109.  
  110.     Public ReadOnly Property Version() As UInt32
  111.         Get
  112.             Return _Version
  113.         End Get
  114.     End Property
  115.  
  116.     Public ReadOnly Property FoldersCount() As Int32
  117.         Get
  118.             Return _FoldersCount
  119.         End Get
  120.     End Property
  121.  
  122.     Public ReadOnly Property FilesCount() As Int32
  123.         Get
  124.             Return _FilesCount
  125.         End Get
  126.     End Property
  127.  
  128.     Public ReadOnly Property ArchiveFlags() As BsaArchiveFlags
  129.         Get
  130.             Return _ArchiveFlags
  131.         End Get
  132.     End Property
  133.  
  134.     Public ReadOnly Property FileTypeFlags() As BsaFileTypeFlags
  135.         Get
  136.             Return _FileTypeFlags
  137.         End Get
  138.     End Property
  139.  
  140.     Public ReadOnly Property Folders() As List(Of BsaFolder)
  141.         Get
  142.             Return _Folders
  143.         End Get
  144.     End Property
  145.  
  146.     Public ReadOnly Property IsCompressionEnabled() As Boolean
  147.         Get
  148.             Return (Me.ArchiveFlags And BsaArchiveFlags.CompressionEnabled) = BsaArchiveFlags.CompressionEnabled
  149.         End Get
  150.     End Property
  151.  
  152.  
  153.     Public Sub Open(ByVal FileName As String)
  154.         If Not IO.File.Exists(FileName) Then
  155.             Throw New IO.FileNotFoundException("File non trovato: " & Me.FileName)
  156.         End If
  157.  
  158.         _FileName = FileName
  159.  
  160.         Stream = New IO.FileStream(Me.FileName, IO.FileMode.Open)
  161.         Dim Reader As New IO.BinaryReader(Stream)
  162.  
  163.         If Reader.ReadChars(3) <> "BSA" Then
  164.             Reader.Close()
  165.             Stream.Close()
  166.             Stream = Nothing
  167.             Throw New FormatException(IO.Path.GetFileName(Me.FileName) & " non è un archivio BSA valido.")
  168.         End If
  169.         Reader.ReadByte()
  170.         _Version = Reader.ReadUInt32()
  171.         Reader.ReadUInt32() 'Offset = 36
  172.         _ArchiveFlags = Reader.ReadUInt32()
  173.         _FoldersCount = Reader.ReadUInt32()
  174.         _FilesCount = Reader.ReadUInt32()
  175.         TotalFolderNamesLength = Reader.ReadUInt32()
  176.         TotalFileNamesLength = Reader.ReadUInt32()
  177.         _FileTypeFlags = Reader.ReadUInt32()
  178.  
  179.         If ((Me.ArchiveFlags And BsaArchiveFlags.HasDirectoryNames) = 0) Or ((Me.ArchiveFlags And BsaArchiveFlags.HasFileNames) = 0) Then
  180.             Reader.Close()
  181.             Stream.Close()
  182.             Stream = Nothing
  183.             Throw New FormatException(IO.Path.GetFileName(Me.FileName) & " contiene tipologie di dati non supportate dal programma.")
  184.         End If
  185.  
  186.         Dim FolderRecords As New List(Of FolderRecord)
  187.         Dim FR As FolderRecord
  188.  
  189.         For I As Int32 = 1 To Me.FoldersCount
  190.             FR = New FolderRecord()
  191.             FR.NameHash = Reader.ReadUInt64()
  192.             FR.Count = Reader.ReadUInt32()
  193.             FR.Offset = Reader.ReadUInt32()
  194.             FolderRecords.Add(FR)
  195.         Next
  196.  
  197.         Dim FileRecordBlocks As New List(Of FileRecordBlock)
  198.         Dim FRB As FileRecordBlock
  199.  
  200.         For I As Int32 = 1 To Me.FoldersCount
  201.             Dim FileR As FileRecord
  202.  
  203.             FRB = New FileRecordBlock()
  204.             FRB.FolderName = Reader.ReadFixedLenString()
  205.  
  206.             ReDim FRB.Records(FolderRecords(I - 1).Count - 1)
  207.             For J As Int32 = 0 To FRB.Records.Length - 1
  208.                 FileR = New FileRecord()
  209.                 FileR.NameHash = Reader.ReadUInt64()
  210.                 FileR.Size = Reader.ReadUInt32()
  211.                 FileR.Offset = Reader.ReadUInt32()
  212.                 FRB.Records(J) = FileR
  213.             Next
  214.  
  215.             FileRecordBlocks.Add(FRB)
  216.         Next
  217.  
  218.         Dim FileNames As New List(Of String)
  219.         For I As Int32 = 1 To Me.FilesCount
  220.             FileNames.Add(Reader.ReadNullTerminatedString())
  221.         Next
  222.  
  223.         Dim FilesIndexed As Int32 = 0
  224.         _Folders = New List(Of BsaFolder)
  225.         For I As Int32 = 0 To Me.FoldersCount - 1
  226.             Dim BFolder As New BsaFolder(FileRecordBlocks(I).FolderName)
  227.             For J As Int32 = 0 To FileRecordBlocks(I).Records.Length - 1
  228.                 Dim BFile As New BsaFile(FileNames(FilesIndexed), FileRecordBlocks(I).Records(J).Offset, FileRecordBlocks(I).Records(J).Size)
  229.                 BFolder.Files.Add(BFile)
  230.                 FilesIndexed += 1
  231.             Next
  232.             Me.Folders.Add(BFolder)
  233.         Next
  234.  
  235.         FolderRecords.Clear()
  236.         FileRecordBlocks.Clear()
  237.         FileNames.Clear()
  238.         FolderRecords = Nothing
  239.         FileRecordBlocks = Nothing
  240.         FileNames = Nothing
  241.         Reader = Nothing
  242.     End Sub
  243.  
  244.     Public Sub ExtractFile(ByVal Ref As BsaFile, ByVal OutputFolder As String)
  245.         Dim Output As IO.Stream
  246.         Dim Buffer(7999) As Byte
  247.         Dim BytesRead As UInt32 = 0
  248.  
  249.         Stream.Position = Ref.Offset
  250.         Output = New IO.FileStream(OutputFolder & "\" & Ref.Name, IO.FileMode.Create)
  251.  
  252.         If (((Me.ArchiveFlags And BsaArchiveFlags.CompressionEnabled) = BsaArchiveFlags.CompressionEnabled) And (Ref.Size < 2 ^ 31)) _
  253.         Or (((Me.ArchiveFlags And BsaArchiveFlags.CompressionEnabled) = 0) And (Ref.Size >= 2 ^ 31)) Then
  254.             If Ref.Size >= 2 ^ 31 Then
  255.                 Ref.Size -= 2 ^ 31
  256.             End If
  257.  
  258.             Stream.Position += 4
  259.  
  260.             Dim CompressedStream As New IO.MemoryStream()
  261.             Dim Buffer2(Ref.Size - 1) As Byte
  262.             Stream.Read(Buffer2, 0, Buffer2.Length)
  263.             CompressedStream.Write(Buffer2, 0, Buffer2.Length)
  264.             CompressedStream.Position = 0
  265.  
  266.             Dim DataReader As New ManagedZLib.Decompress(CompressedStream)
  267.             Dim DataWriter = New IO.BinaryWriter(Output)
  268.  
  269.             Try
  270.                 BytesRead = DataReader.Read(Buffer, 0, Buffer.Length)
  271.                 Do While (BytesRead > 0)
  272.                     DataWriter.Write(Buffer, 0, BytesRead)
  273.                     BytesRead = DataReader.Read(Buffer, 0, Buffer.Length)
  274.                 Loop
  275.             Catch Ex As Exception
  276.                 Throw Ex
  277.             Finally
  278.                 DataWriter.Close()
  279.                 DataReader.Close()
  280.             End Try
  281.         Else
  282.             Dim TotalBytesRead As UInt32 = 0
  283.  
  284.             Do
  285.                 If Ref.Size - TotalBytesRead < Buffer.Length Then
  286.                     BytesRead = Stream.Read(Buffer, 0, Ref.Size - TotalBytesRead)
  287.                 Else
  288.                     BytesRead = Stream.Read(Buffer, 0, Buffer.Length)
  289.                 End If
  290.                 Output.Write(Buffer, 0, BytesRead)
  291.                 TotalBytesRead += BytesRead
  292.             Loop Until TotalBytesRead >= Ref.Size
  293.         End If
  294.  
  295.         Output.Close()
  296.     End Sub
  297.  
  298.     Public Sub Close()
  299.         _ArchiveFlags = 0
  300.         _FilesCount = 0
  301.         _FileTypeFlags = 0
  302.         _Folders.Clear()
  303.         _Folders = Nothing
  304.         _FoldersCount = 0
  305.         _Version = 0
  306.         Stream.Close()
  307.     End Sub
  308. End Class
  309.  
  310. Module Globals
  311.  
  312.     <System.Runtime.CompilerServices.Extension()> _
  313.     Public Function ReadNullTerminatedString(ByVal Reader As IO.BinaryReader) As String
  314.         Dim S As New System.Text.StringBuilder()
  315.         Dim B As Byte
  316.  
  317.         B = Reader.ReadByte()
  318.         Do While B <> 0
  319.             S.Append(Chr(B))
  320.             B = Reader.ReadByte()
  321.         Loop
  322.  
  323.         Return S.ToString()
  324.     End Function
  325.  
  326.     <System.Runtime.CompilerServices.Extension()> _
  327.     Public Function ReadFixedLenString(ByVal Reader As IO.BinaryReader) As String
  328.         Dim S As New System.Text.StringBuilder()
  329.         Dim Len As Byte
  330.         Dim B As Byte
  331.  
  332.         Len = Reader.ReadByte()
  333.         For I As Byte = 1 To Len - 1
  334.             B = Reader.ReadByte()
  335.             S.Append(Chr(B))
  336.         Next
  337.         Reader.ReadByte() ' = Null
  338.  
  339.         Return S.ToString()
  340.     End Function
  341.  
  342.     <System.Runtime.CompilerServices.Extension()> _
  343.     Public Sub AddItem(ByVal List As ListView, ByVal ParamArray SubItemTexts() As String)
  344.         Dim L As New ListViewItem(SubItemTexts)
  345.         List.Items.Add(L)
  346.     End Sub
  347.  
  348. End Module