mirror of
https://github.com/simtactics/mysimulation.git
synced 2025-03-15 14:51:21 +00:00
243 lines
8.6 KiB
C#
Executable file
243 lines
8.6 KiB
C#
Executable file
using System;
|
|
using System.Collections.Generic;
|
|
using System.Text;
|
|
using System.IO;
|
|
|
|
namespace FSO.Files.FAR3
|
|
{
|
|
/// <summary>
|
|
/// Represents a single FAR3 archive.
|
|
/// </summary>
|
|
public class FAR3Archive : IDisposable
|
|
{
|
|
private BinaryReader m_Reader;
|
|
public static bool isReadingSomething = false;
|
|
|
|
private string m_ArchivePath;
|
|
private Dictionary<string, Far3Entry> m_Entries = new Dictionary<string, Far3Entry>();
|
|
private List<Far3Entry> m_EntriesList = new List<Far3Entry>();
|
|
private Dictionary<uint, Far3Entry> m_EntryByID = new Dictionary<uint, Far3Entry>();
|
|
private uint m_ManifestOffset;
|
|
|
|
/// <summary>
|
|
/// Creates a new FAR3Archive instance from a path.
|
|
/// </summary>
|
|
/// <param name="Path">The path to the archive.</param>
|
|
public FAR3Archive(string Path)
|
|
{
|
|
m_ArchivePath = Path;
|
|
|
|
if (isReadingSomething == false)
|
|
{
|
|
isReadingSomething = true;
|
|
|
|
try
|
|
{
|
|
m_Reader = new BinaryReader(File.Open(Path, FileMode.Open, FileAccess.Read, FileShare.Read));
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
throw new FAR3Exception("Could not open the specified archive - " + Path + "! (FAR3Archive())");
|
|
}
|
|
|
|
string Header = Encoding.ASCII.GetString(m_Reader.ReadBytes(8));
|
|
uint Version = m_Reader.ReadUInt32();
|
|
|
|
if ((Header != "FAR!byAZ") || (Version != 3))
|
|
{
|
|
throw new FAR3Exception("Archive wasn't a valid FAR V.3 archive! (FAR3Archive())");
|
|
}
|
|
|
|
uint ManifestOffset = m_Reader.ReadUInt32();
|
|
m_ManifestOffset = ManifestOffset;
|
|
|
|
m_Reader.BaseStream.Seek(ManifestOffset, SeekOrigin.Begin);
|
|
|
|
uint NumFiles = m_Reader.ReadUInt32();
|
|
|
|
for (int i = 0; i < NumFiles; i++)
|
|
{
|
|
Far3Entry Entry = new Far3Entry();
|
|
Entry.DecompressedFileSize = m_Reader.ReadUInt32();
|
|
byte dummy0 = m_Reader.ReadByte();
|
|
byte dummy1 = m_Reader.ReadByte();
|
|
byte dummy2 = m_Reader.ReadByte();
|
|
Entry.CompressedFileSize = (uint)((dummy0 << 0) | (dummy1 << 8) | (dummy2) << 16);
|
|
Entry.DataType = m_Reader.ReadByte();
|
|
Entry.DataOffset = m_Reader.ReadUInt32();
|
|
//Entry.HasFilename = m_Reader.ReadUInt16();
|
|
Entry.IsCompressed = m_Reader.ReadByte();
|
|
Entry.AccessNumber = m_Reader.ReadByte();
|
|
Entry.FilenameLength = m_Reader.ReadUInt16();
|
|
Entry.TypeID = m_Reader.ReadUInt32();
|
|
Entry.FileID = m_Reader.ReadUInt32();
|
|
Entry.Filename = Encoding.ASCII.GetString(m_Reader.ReadBytes(Entry.FilenameLength));
|
|
|
|
if (!m_Entries.ContainsKey(Entry.Filename))
|
|
m_Entries.Add(Entry.Filename, Entry);
|
|
m_EntriesList.Add(Entry);
|
|
|
|
m_EntryByID.Add(Entry.FileID, Entry); //isn't this a bad idea? i have a feeling this is a bad idea...
|
|
}
|
|
|
|
//Keep the stream open, it helps peformance.
|
|
//m_Reader.Close();
|
|
isReadingSomething = false;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets an entry's data from a Far3Entry instance.
|
|
/// </summary>
|
|
/// <param name="Entry">The Far3Entry instance.</param>
|
|
/// <returns>The entry's data.</returns>
|
|
public byte[] GetEntry(Far3Entry Entry)
|
|
{
|
|
lock (m_Reader)
|
|
{
|
|
m_Reader.BaseStream.Seek((long)Entry.DataOffset, SeekOrigin.Begin);
|
|
|
|
isReadingSomething = true;
|
|
|
|
if (Entry.IsCompressed == 0x01)
|
|
{
|
|
m_Reader.BaseStream.Seek(9, SeekOrigin.Current);
|
|
uint Filesize = m_Reader.ReadUInt32();
|
|
ushort CompressionID = m_Reader.ReadUInt16();
|
|
|
|
if (CompressionID == 0xFB10)
|
|
{
|
|
byte dummy0 = m_Reader.ReadByte();
|
|
byte dummy1 = m_Reader.ReadByte();
|
|
byte dummy2 = m_Reader.ReadByte();
|
|
uint DecompressedSize = (uint)((dummy0 << 0x10) | (dummy1 << 0x08) | +dummy2);
|
|
|
|
Decompresser Dec = new Decompresser();
|
|
Dec.CompressedSize = Filesize;
|
|
Dec.DecompressedSize = DecompressedSize;
|
|
|
|
byte[] DecompressedData = Dec.Decompress(m_Reader.ReadBytes((int)Filesize));
|
|
//m_Reader.Close();
|
|
|
|
isReadingSomething = false;
|
|
|
|
return DecompressedData;
|
|
}
|
|
else
|
|
{
|
|
m_Reader.BaseStream.Seek((m_Reader.BaseStream.Position - 15), SeekOrigin.Begin);
|
|
|
|
byte[] Data = m_Reader.ReadBytes((int)Entry.DecompressedFileSize);
|
|
//m_Reader.Close();
|
|
|
|
isReadingSomething = false;
|
|
|
|
return Data;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
byte[] Data = m_Reader.ReadBytes((int)Entry.DecompressedFileSize);
|
|
//m_Reader.Close();
|
|
|
|
isReadingSomething = false;
|
|
|
|
return Data;
|
|
}
|
|
}
|
|
|
|
throw new FAR3Exception("FAR3Entry didn't exist in archive - FAR3Archive.GetEntry()");
|
|
}
|
|
|
|
/// <summary>
|
|
/// Returns the entries of this FAR3Archive as byte arrays together with their corresponding FileIDs.
|
|
/// </summary>
|
|
/// <returns>A List of KeyValuePair instances.</returns>
|
|
public List<KeyValuePair<uint, byte[]>> GetAllEntries()
|
|
{
|
|
List<KeyValuePair<uint, byte[]>> toReturn = new List<KeyValuePair<uint, byte[]>>();
|
|
foreach (Far3Entry Entry in m_EntriesList)
|
|
{
|
|
toReturn.Add(new KeyValuePair<uint, byte[]>(Entry.FileID, GetEntry(Entry)));
|
|
}
|
|
return toReturn;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Returns the entries of this FAR3Archive as FAR3Entry instances in a List.
|
|
/// </summary>
|
|
/// <returns>Returns the entries of this FAR3Archive as FAR3Entry instances in a List.</returns>
|
|
public List<Far3Entry> GetAllFAR3Entries()
|
|
{
|
|
List<Far3Entry> Entries = new List<Far3Entry>();
|
|
|
|
foreach (KeyValuePair<string, Far3Entry> KVP in m_Entries)
|
|
Entries.Add(KVP.Value);
|
|
|
|
return Entries;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets an entry from a FileID.
|
|
/// </summary>
|
|
/// <param name="FileID">The entry's FileID.</param>
|
|
/// <returns>The entry's data.</returns>
|
|
public byte[] GetItemByID(uint FileID)
|
|
{
|
|
var item = m_EntryByID[FileID];
|
|
if (item == null)
|
|
{
|
|
throw new FAR3Exception("Didn't find entry!");
|
|
}
|
|
return GetEntry(item);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets an entry from its ID (TypeID + FileID).
|
|
/// </summary>
|
|
/// <param name="ID">The ID of the entry.</param>
|
|
/// <returns>The entry's data.</returns>
|
|
public byte[] GetItemByID(ulong ID)
|
|
{
|
|
byte[] Bytes = BitConverter.GetBytes(ID);
|
|
uint FileID = BitConverter.ToUInt32(Bytes, 4);
|
|
uint TypeID = BitConverter.ToUInt32(Bytes, 0);
|
|
|
|
var item = m_EntryByID[FileID];
|
|
if (item == null || item.TypeID != TypeID)
|
|
{
|
|
throw new FAR3Exception("Didn't find entry!");
|
|
}
|
|
|
|
return GetEntry(item);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets an entry's data from a filename.
|
|
/// </summary>
|
|
/// <param name="Filename">The filename of the entry.</param>
|
|
/// <returns>The entry's data.</returns>
|
|
public byte[] this[string Filename]
|
|
{
|
|
get
|
|
{
|
|
return GetEntry(m_Entries[Filename]);
|
|
}
|
|
}
|
|
|
|
#region IDisposable Members
|
|
|
|
/// <summary>
|
|
/// Disposes this FAR3Archive instance.
|
|
/// </summary>
|
|
public void Dispose()
|
|
{
|
|
if (m_Reader != null)
|
|
{
|
|
m_Reader.Close();
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
}
|
|
}
|