132 lines
4.9 KiB
C#
132 lines
4.9 KiB
C#
using System;
|
|
using System.IO;
|
|
|
|
namespace Lucidiot.Magellan {
|
|
/// <summary>
|
|
/// Represents a single file entry in an Magellan map archive.
|
|
/// </summary>
|
|
public struct MapArchiveEntry {
|
|
private string _name;
|
|
|
|
/// <summary>
|
|
/// Name of the file, without the extension. Up to 8 ASCII characters.
|
|
/// </summary>
|
|
public string Name {
|
|
get { return _name; }
|
|
set {
|
|
CheckString("Name", value, 8);
|
|
_name = value;
|
|
}
|
|
}
|
|
|
|
private string _ext;
|
|
|
|
/// <summary>
|
|
/// File extension. Up to 7 ASCII characters.
|
|
/// </summary>
|
|
/// <remarks>
|
|
/// While the format allows enough space for file extensions that go up to 7 ASCII characters,
|
|
/// only two to three characters have been seen in the wild. Not all existing implementations
|
|
/// of the format might support more than three characters.
|
|
/// </remarks>
|
|
public string Extension {
|
|
get { return _ext; }
|
|
set {
|
|
CheckString("Extension", value, 7);
|
|
_ext = value;
|
|
}
|
|
}
|
|
|
|
public string FullName {
|
|
get {
|
|
if (Extension == null || Extension == String.Empty)
|
|
return Name;
|
|
return String.Format("{0}.{1}", Name, Extension);
|
|
}
|
|
}
|
|
|
|
private uint _offset;
|
|
|
|
/// <summary>
|
|
/// Offset of the file's contents within the archive.
|
|
/// Should be an even number.
|
|
/// </summary>
|
|
public uint Offset {
|
|
get { return _offset; }
|
|
set { _offset = value; }
|
|
}
|
|
|
|
private uint _length;
|
|
|
|
/// <summary>
|
|
/// Length of the file's contents within the archive.
|
|
/// If the length is an odd number, an extra null byte is added after the file's contents
|
|
/// to ensure an even offset for the next file.
|
|
/// </summary>
|
|
public uint Length {
|
|
get { return _length; }
|
|
set { _length = value; }
|
|
}
|
|
|
|
private static void CheckString(string paramName, string value, int maxLength) {
|
|
if (value.Length > maxLength || !Helpers.IsASCII(value))
|
|
throw new ArgumentOutOfRangeException(paramName, "Only " + maxLength + " ASCII characters are supported.");
|
|
}
|
|
|
|
public MapArchiveEntry(string Name, string Extension, uint Offset, uint Length) {
|
|
CheckString("Name", Name, 8);
|
|
CheckString("Extension", Extension, 7);
|
|
|
|
this._name = Name;
|
|
this._ext = Extension;
|
|
this._offset = Offset;
|
|
this._length = Length;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Parse a single MapArchiveEntry from a BinaryReader, leaving the reader's cursor positioned right after the entry.
|
|
/// </summary>
|
|
/// <param name="br">The BinaryReader to read from.</param>
|
|
/// <returns>A parsed MapArchiveEntry.</returns>
|
|
public static MapArchiveEntry Parse(BinaryReader br) {
|
|
string name = new string(br.ReadChars(8)).Trim("\0".ToCharArray());
|
|
br.ReadByte();
|
|
string ext = new string(br.ReadChars(7)).Trim("\0".ToCharArray());
|
|
uint offset = br.ReadUInt32();
|
|
uint length = br.ReadUInt32();
|
|
return new MapArchiveEntry(name, ext, offset, length);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Maximum size of the buffer used to transfer data from a BinaryReader to a BinaryWriter during extraction.
|
|
/// Note that the buffer may be smaller if the MapArchiveEntry's length is lower than this size.
|
|
/// </summary>
|
|
private const int EXTRACT_BUFFER_SIZE = 32768;
|
|
|
|
/// <summary>
|
|
/// Extract the file designated by this entry from the archive into a file on the filesystem,
|
|
/// named after the name and extension set in the entry.
|
|
/// </summary>
|
|
/// <param name="br">The BinaryReader to extract from.</param>
|
|
/// <param name="destination">Existing directory where the file will be extracted.</param>
|
|
/// <exception cref="IOException">An error occurred while reading from the archive, creating the new file, or writing to the new file.</exception>
|
|
public void Extract(BinaryReader br, string destination) {
|
|
if (Length == 0) {
|
|
File.Create(destination).Close();
|
|
return;
|
|
}
|
|
|
|
br.BaseStream.Seek(Offset, SeekOrigin.Begin);
|
|
using (BinaryWriter bw = new BinaryWriter(File.Open(destination, FileMode.Create))) {
|
|
byte[] buffer = new byte[Math.Min(Length, EXTRACT_BUFFER_SIZE)];
|
|
uint copied = 0;
|
|
while (copied < Length) {
|
|
int toCopy = (int)Math.Min(Length - copied, EXTRACT_BUFFER_SIZE);
|
|
copied += (uint)br.Read(buffer, 0, toCopy);
|
|
bw.Write(buffer, 0, toCopy);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|