Files
OpenRA/OpenRA.Mods.Cnc/FileSystem/BigFile.cs
RoosterDragon b98123d9f8 Use SegmentStream.CreateWithoutOwningStream to avoid reading data into memory.
To avoid creating copied data in memory (e.g. via MemoryStream), this method can be used to reference offsets on files on disk, reducing memory requirements.
2019-10-24 20:48:14 +02:00

124 lines
2.7 KiB
C#

#region Copyright & License Information
/*
* Copyright 2007-2019 The OpenRA Developers (see AUTHORS)
* This file is part of OpenRA, which is free software. It is made
* available to you under the terms of the GNU General Public License
* as published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version. For more
* information, see COPYING.
*/
#endregion
using System;
using System.Collections.Generic;
using System.IO;
using OpenRA.FileSystem;
using OpenRA.Primitives;
using FS = OpenRA.FileSystem.FileSystem;
namespace OpenRA.Mods.Cnc.FileSystem
{
public class BigLoader : IPackageLoader
{
sealed class BigFile : IReadOnlyPackage
{
public string Name { get; private set; }
public IEnumerable<string> Contents { get { return index.Keys; } }
readonly Dictionary<string, Entry> index = new Dictionary<string, Entry>();
readonly Stream s;
public BigFile(Stream s, string filename)
{
Name = filename;
this.s = s;
try
{
/* var signature = */ s.ReadASCII(4);
// Total archive size.
s.ReadUInt32();
var entryCount = s.ReadUInt32();
if (BitConverter.IsLittleEndian)
entryCount = int2.Swap(entryCount);
// First entry offset? This is apparently bogus for EA's .big files
// and we don't have to try seeking there since the entries typically start next in EA's .big files.
s.ReadUInt32();
for (var i = 0; i < entryCount; i++)
{
var entry = new Entry(s);
index.Add(entry.Path, entry);
}
}
catch
{
Dispose();
throw;
}
}
class Entry
{
public readonly uint Offset;
public readonly uint Size;
public readonly string Path;
public Entry(Stream s)
{
Offset = s.ReadUInt32();
Size = s.ReadUInt32();
if (BitConverter.IsLittleEndian)
{
Offset = int2.Swap(Offset);
Size = int2.Swap(Size);
}
Path = s.ReadASCIIZ();
}
}
public Stream GetStream(string filename)
{
var entry = index[filename];
return SegmentStream.CreateWithoutOwningStream(s, entry.Offset, (int)entry.Size);
}
public bool Contains(string filename)
{
return index.ContainsKey(filename);
}
public IReadOnlyPackage OpenPackage(string filename, FS context)
{
// Not implemented
return null;
}
public void Dispose()
{
s.Dispose();
}
}
bool IPackageLoader.TryParsePackage(Stream s, string filename, FS context, out IReadOnlyPackage package)
{
// Take a peek at the file signature
var signature = s.ReadASCII(4);
s.Position -= 4;
if (signature != "BIGF")
{
package = null;
return false;
}
package = new BigFile(s, filename);
return true;
}
}
}