using System;
using System.Collections.Generic;
using System.Linq;
using System.IO;
using FSO.Files.Formats.IFF.Chunks;
using FSO.Files.Utils;
using FSO.Common.Utils;
namespace FSO.Files.Formats.IFF
{
///
/// Interchange File Format (IFF) is a chunk-based file format for binary resource data
/// intended to promote a common model for store and use by an executable.
///
public class IffFile : IFileInfoUtilizer, ITimedCachable
{
///
/// Set to true to force the game to retain a copy of all chunk data at time of loading (used to generate piffs)
/// Should really only be set when the user wants to use the IDE, as it uses a lot more memory.
///
public static bool RETAIN_CHUNK_DATA = false;
public static bool TargetTS1 = false;
public bool TSBO = false;
public bool RetainChunkData = RETAIN_CHUNK_DATA;
public object CachedJITModule; //for JIT and AOT modes
public uint ExecutableHash; //hash of BHAV and BCON chunks
public string Filename;
public static Dictionary CHUNK_TYPES = new Dictionary()
{
{"STR#", typeof(STR)},
{"CTSS", typeof(CTSS)},
{"PALT", typeof(PALT)},
{"OBJD", typeof(OBJD)},
{"DGRP", typeof(DGRP)},
{"SPR#", typeof(SPR)},
{"SPR2", typeof(SPR2)},
{"BHAV", typeof(BHAV)},
{"TPRP", typeof(TPRP)},
{"SLOT", typeof(SLOT)},
{"GLOB", typeof(GLOB)},
{"BCON", typeof(BCON)},
{"TTAB", typeof(TTAB)},
{"OBJf", typeof(OBJf)},
{"TTAs", typeof(TTAs)},
{"FWAV", typeof(FWAV)},
{"BMP_", typeof(BMP)},
{"PIFF", typeof(PIFF) },
{"TRCN", typeof(TRCN) },
{"objt", typeof(OBJT) },
{"Arry", typeof(ARRY) },
{"ObjM", typeof(OBJM) },
{"WALm", typeof(WALm) },
{"FLRm", typeof(FLRm) },
{"CARR", typeof(CARR) },
{"NBRS", typeof(NBRS) },
{"FAMI", typeof(FAMI) },
{"NGBH", typeof(NGBH) },
{"FAMs", typeof(FAMs) },
{"THMB", typeof(THMB) },
{"SIMI", typeof(SIMI) },
{"TATT", typeof(TATT) },
{"HOUS", typeof(HOUS) },
//todo: FAMh (family motives ("family house"?)) field encoded.
{"TREE", typeof(TREE) },
{"FCNS", typeof(FCNS) },
{"FSOR", typeof(FSOR) },
{"FSOM", typeof(FSOM) },
{"MTEX", typeof(MTEX) },
{"FSOV", typeof(FSOV) },
{"PNG_", typeof(PNG) }
};
public IffRuntimeInfo RuntimeInfo = new IffRuntimeInfo();
private Dictionary> ByChunkId;
private Dictionary> ByChunkType;
public List RemovedOriginal = new List();
public PIFF CurrentPIFF;
///
/// Constructs a new IFF instance.
///
public IffFile()
{
ByChunkId = new Dictionary>();
ByChunkType = new Dictionary>();
}
///
/// Constructs an IFF instance from a filepath.
///
/// Path to the IFF.
public IffFile(string filepath) : this()
{
using (var stream = File.Open(filepath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
{
this.Read(stream);
SetFilename(Path.GetFileName(filepath));
}
}
///
/// Constructs an IFF instance from a filepath.
///
/// Path to the IFF.
public IffFile(string filepath, bool retainData) : this()
{
RetainChunkData = retainData;
using (var stream = File.Open(filepath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
{
this.Read(stream);
SetFilename(Path.GetFileName(filepath));
}
}
private bool WasReferenced = true;
~IffFile()
{
if (WasReferenced)
{
TimedReferenceController.KeepAlive(this, KeepAliveType.DEREFERENCED);
WasReferenced = false;
GC.ReRegisterForFinalize(this);
} else
{
var all = SilentListAll();
foreach (var chunk in all)
{
chunk.Dispose();
}
}
}
public void Rereferenced(bool saved)
{
WasReferenced = saved;
}
public void MarkThrowaway()
{
WasReferenced = false;
}
///
/// Reads an IFF from a stream.
///
/// The stream to read from.
public void Read(Stream stream)
{
using (var io = IoBuffer.FromStream(stream, ByteOrder.BIG_ENDIAN))
{
var identifier = io.ReadCString(60, false).Replace("\0", "");
if (identifier != "IFF FILE 2.5:TYPE FOLLOWED BY SIZE JAMIE DOORNBOS & MAXIS 1")
{
if (identifier != "IFF FILE 2.0:TYPE FOLLOWED BY SIZE JAMIE DOORNBOS & MAXIS 1") //house11.iff, seems to read fine
throw new Exception("Invalid iff file!");
}
var rsmpOffset = io.ReadUInt32();
while (io.HasMore)
{
var newChunk = AddChunk(stream, io, true);
}
}
}
public void InitHash()
{
if (ExecutableHash != 0) return;
if (ByChunkType.ContainsKey(typeof(BHAV)))
{
IEnumerable