mirror of
https://github.com/simtactics/mysimulation.git
synced 2025-03-19 08:21:22 +00:00
185 lines
5.7 KiB
C#
185 lines
5.7 KiB
C#
|
using System;
|
|||
|
using System.Collections.Generic;
|
|||
|
using System.IO;
|
|||
|
using FSO.Files.Utils;
|
|||
|
|
|||
|
namespace FSO.Files.Formats.DBPF
|
|||
|
{
|
|||
|
/// <summary>
|
|||
|
/// The database-packed file (DBPF) is a format used to store data for pretty much all Maxis games after The Sims,
|
|||
|
/// including The Sims Online (the first appearance of this format), SimCity 4, The Sims 2, Spore, The Sims 3, and
|
|||
|
/// SimCity 2013.
|
|||
|
/// </summary>
|
|||
|
public class DBPFFile : IDisposable
|
|||
|
{
|
|||
|
public int DateCreated;
|
|||
|
public int DateModified;
|
|||
|
|
|||
|
private uint IndexMajorVersion;
|
|||
|
private uint NumEntries;
|
|||
|
private IoBuffer m_Reader;
|
|||
|
|
|||
|
private List<DBPFEntry> m_EntriesList = new List<DBPFEntry>();
|
|||
|
private Dictionary<ulong, DBPFEntry> m_EntryByID = new Dictionary<ulong, DBPFEntry>();
|
|||
|
private Dictionary<DBPFTypeID, List<DBPFEntry>> m_EntriesByType = new Dictionary<DBPFTypeID, List<DBPFEntry>>();
|
|||
|
|
|||
|
private IoBuffer Io;
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Constructs a new DBPF instance.
|
|||
|
/// </summary>
|
|||
|
public DBPFFile()
|
|||
|
{
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Creates a DBPF instance from a path.
|
|||
|
/// </summary>
|
|||
|
/// <param name="file">The path to an DBPF archive.</param>
|
|||
|
public DBPFFile(string file)
|
|||
|
{
|
|||
|
var stream = File.OpenRead(file);
|
|||
|
Read(stream);
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Reads a DBPF archive from a stream.
|
|||
|
/// </summary>
|
|||
|
/// <param name="stream">The stream to read from.</param>
|
|||
|
public void Read(Stream stream)
|
|||
|
{
|
|||
|
m_EntryByID = new Dictionary<ulong,DBPFEntry>();
|
|||
|
m_EntriesList = new List<DBPFEntry>();
|
|||
|
|
|||
|
var io = IoBuffer.FromStream(stream, ByteOrder.LITTLE_ENDIAN);
|
|||
|
m_Reader = io;
|
|||
|
this.Io = io;
|
|||
|
|
|||
|
var magic = io.ReadCString(4);
|
|||
|
if (magic != "DBPF")
|
|||
|
{
|
|||
|
throw new Exception("Not a DBPF file");
|
|||
|
}
|
|||
|
|
|||
|
var majorVersion = io.ReadUInt32();
|
|||
|
var minorVersion = io.ReadUInt32();
|
|||
|
var version = majorVersion + (((double)minorVersion)/10.0);
|
|||
|
|
|||
|
/** Unknown, set to 0 **/
|
|||
|
io.Skip(12);
|
|||
|
|
|||
|
if (version == 1.0)
|
|||
|
{
|
|||
|
this.DateCreated = io.ReadInt32();
|
|||
|
this.DateModified = io.ReadInt32();
|
|||
|
}
|
|||
|
|
|||
|
if (version < 2.0)
|
|||
|
{
|
|||
|
IndexMajorVersion = io.ReadUInt32();
|
|||
|
}
|
|||
|
|
|||
|
NumEntries = io.ReadUInt32();
|
|||
|
uint indexOffset = 0;
|
|||
|
if (version < 2.0)
|
|||
|
{
|
|||
|
indexOffset = io.ReadUInt32();
|
|||
|
}
|
|||
|
var indexSize = io.ReadUInt32();
|
|||
|
|
|||
|
if (version < 2.0)
|
|||
|
{
|
|||
|
var trashEntryCount = io.ReadUInt32();
|
|||
|
var trashIndexOffset = io.ReadUInt32();
|
|||
|
var trashIndexSize = io.ReadUInt32();
|
|||
|
var indexMinor = io.ReadUInt32();
|
|||
|
}
|
|||
|
else if (version == 2.0)
|
|||
|
{
|
|||
|
var indexMinor = io.ReadUInt32();
|
|||
|
indexOffset = io.ReadUInt32();
|
|||
|
io.Skip(4);
|
|||
|
}
|
|||
|
|
|||
|
/** Padding **/
|
|||
|
io.Skip(32);
|
|||
|
|
|||
|
io.Seek(SeekOrigin.Begin, indexOffset);
|
|||
|
for (int i = 0; i < NumEntries; i++)
|
|||
|
{
|
|||
|
var entry = new DBPFEntry();
|
|||
|
entry.TypeID = (DBPFTypeID)io.ReadUInt32();
|
|||
|
entry.GroupID = (DBPFGroupID)io.ReadUInt32();
|
|||
|
entry.InstanceID = io.ReadUInt32();
|
|||
|
entry.FileOffset = io.ReadUInt32();
|
|||
|
entry.FileSize = io.ReadUInt32();
|
|||
|
|
|||
|
m_EntriesList.Add(entry);
|
|||
|
ulong id = (((ulong)entry.InstanceID) << 32) + (ulong)entry.TypeID;
|
|||
|
if (!m_EntryByID.ContainsKey(id))
|
|||
|
m_EntryByID.Add(id, entry);
|
|||
|
|
|||
|
if (!m_EntriesByType.ContainsKey(entry.TypeID))
|
|||
|
m_EntriesByType.Add(entry.TypeID, new List<DBPFEntry>());
|
|||
|
|
|||
|
m_EntriesByType[entry.TypeID].Add(entry);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Gets a DBPFEntry's data from this DBPF instance.
|
|||
|
/// </summary>
|
|||
|
/// <param name="entry">Entry to retrieve data for.</param>
|
|||
|
/// <returns>Data for entry.</returns>
|
|||
|
public byte[] GetEntry(DBPFEntry entry)
|
|||
|
{
|
|||
|
m_Reader.Seek(SeekOrigin.Begin, entry.FileOffset);
|
|||
|
|
|||
|
return m_Reader.ReadBytes((int)entry.FileSize);
|
|||
|
}
|
|||
|
|
|||
|
/// <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)
|
|||
|
{
|
|||
|
if (m_EntryByID.ContainsKey(ID))
|
|||
|
return GetEntry(m_EntryByID[ID]);
|
|||
|
else
|
|||
|
return null;
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Gets all entries of a specific type.
|
|||
|
/// </summary>
|
|||
|
/// <param name="Type">The Type of the entry.</param>
|
|||
|
/// <returns>The entry data, paired with its instance id.</returns>
|
|||
|
public List<KeyValuePair<uint, byte[]>> GetItemsByType(DBPFTypeID Type)
|
|||
|
{
|
|||
|
|
|||
|
var result = new List<KeyValuePair<uint, byte[]>>();
|
|||
|
|
|||
|
var entries = m_EntriesByType[Type];
|
|||
|
for (int i = 0; i < entries.Count; i++)
|
|||
|
{
|
|||
|
result.Add(new KeyValuePair<uint, byte[]>(entries[i].InstanceID, GetEntry(entries[i])));
|
|||
|
}
|
|||
|
return result;
|
|||
|
}
|
|||
|
|
|||
|
#region IDisposable Members
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Disposes this DBPF instance.
|
|||
|
/// </summary>
|
|||
|
public void Dispose()
|
|||
|
{
|
|||
|
Io.Dispose();
|
|||
|
}
|
|||
|
|
|||
|
#endregion
|
|||
|
}
|
|||
|
}
|