Files
OpenRA/OpenRa.FileFormats/Package.cs
bob 1438053505 - bugfix in Format80
- fixed heisenburg-endianness in map loader
    - THERES A BUG in the mix loading; I need another 4 bytes padding to load temperat.mix and snow.mix (not interior.mix, though)
    - ShpViewer can now load and view map files
    - Copy TEMPERAT, SNOW, INFERIOR (sic) mixes into $(SolutionDir) for this to work
    - Left-click to reload tile-ID file, middle-click scrolls
    - the tile-id file has some collisions between tile-sets, be careful about ordering if you change anything


git-svn-id: svn://svn.ijw.co.nz/svn/OpenRa@1081 993157c7-ee19-0410-b2c4-bb4e9862e678
2007-06-26 21:25:20 +00:00

155 lines
3.6 KiB
C#

using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
using OpenRa.Core;
namespace OpenRa.FileFormats
{
public class Package
{
readonly string filename;
readonly List<PackageEntry> index;
readonly bool isRmix, isEncrypted;
public ICollection<PackageEntry> Content
{
get { return index.AsReadOnly(); }
}
public Package(string filename)
{
this.filename = filename;
using (Stream s = File.OpenRead(filename))
{
BinaryReader reader = new BinaryReader(s);
uint signature = reader.ReadUInt32();
isRmix = 0 == (signature & ~(uint)(MixFileFlags.Checksum | MixFileFlags.Encrypted));
if (isRmix)
{
isEncrypted = 0 != (signature & (uint)MixFileFlags.Encrypted);
index = ParseRaHeader(s);
}
else
{
isEncrypted = false;
s.Seek(0, SeekOrigin.Begin);
index = ParseTdHeader(s);
}
}
}
List<PackageEntry> ParseRaHeader(Stream s)
{
if (!isEncrypted)
{
Console.WriteLine("RA, not encrypted");
return ParseTdHeader(s);
}
long headerStart = 84;
BinaryReader reader = new BinaryReader(s);
byte[] keyblock = reader.ReadBytes(80);
byte[] blowfishKey = MixDecrypt.MixDecrypt.BlowfishKey(keyblock);
uint[] h = ReadUints(reader, 2);
Blowfish fish = new Blowfish(blowfishKey);
uint[] decrypted = fish.Decrypt(h);
MemoryStream ms = new MemoryStream();
BinaryWriter writer = new BinaryWriter(ms);
foreach (uint t in decrypted)
writer.Write(t);
writer.Flush();
ms.Position = 0;
BinaryReader reader2 = new BinaryReader(ms);
ushort numFiles = reader2.ReadUInt16();
uint datasize = reader2.ReadUInt32();
Console.WriteLine("{0} files, {1} kb", numFiles, datasize >> 10);
s.Position = headerStart;
reader = new BinaryReader(s);
h = ReadUints(reader, 2 + numFiles * PackageEntry.Size / 4);
decrypted = fish.Decrypt(h);
ms = new MemoryStream();
writer = new BinaryWriter(ms);
foreach (uint t in decrypted)
writer.Write(t);
writer.Flush();
ms.Position = 0;
return ParseTdHeader(ms);
}
uint[] ReadUints(BinaryReader r, int count)
{
uint[] ret = new uint[count];
for (int i = 0; i < ret.Length; i++)
ret[i] = r.ReadUInt32();
return ret;
}
List<PackageEntry> ParseTdHeader(Stream s)
{
List<PackageEntry> items = new List<PackageEntry>();
BinaryReader reader = new BinaryReader(s);
ushort numFiles = reader.ReadUInt16();
uint dataSize = reader.ReadUInt32();
for (int i = 0; i < numFiles; i++)
items.Add(new PackageEntry(reader));
return items;
}
public Stream GetContent(uint hash)
{
foreach( PackageEntry e in index )
if (e.Hash == hash)
{
using (Stream s = File.OpenRead(filename))
{
s.Seek(2 + 4 + (isRmix ? 4 : 0), SeekOrigin.Begin);
s.Seek(2, SeekOrigin.Current); //dword align
s.Seek( 4, SeekOrigin.Current ); //wtf, i dont know why i need this either :(
if( isEncrypted )
s.Seek(80, SeekOrigin.Current);
s.Seek( index.Count * PackageEntry.Size + e.Offset, SeekOrigin.Current );
byte[] data = new byte[ e.Length ];
s.Read( data, 0, (int)e.Length );
return new MemoryStream(data);
}
}
throw new FileNotFoundException();
}
public Stream GetContent(string filename)
{
return GetContent(PackageEntry.HashFilename(filename));
}
}
[Flags]
enum MixFileFlags : uint
{
Checksum = 0x10000,
Encrypted = 0x20000,
}
}