Initial commit.

This commit is contained in:
Poddav
2014-07-21 23:26:28 +04:00
commit e208029dd3
102 changed files with 14809 additions and 0 deletions

245
GameRes/ArcFile.cs Normal file
View File

@@ -0,0 +1,245 @@
//! \file ArcFile.cs
//! \date Tue Jul 08 12:53:45 2014
//! \brief Game Archive file class.
//
using System;
using System.IO;
using System.Linq;
using System.Collections.Generic;
using System.Diagnostics;
namespace GameRes
{
public enum ExtractAction
{
Abort,
Skip,
Continue,
}
public class ArcFile : IDisposable
{
private ArcView m_arc;
private ArchiveFormat m_interface;
private ICollection<Entry> m_dir;
/// <summary>Tag that identifies this archive format.</summary>
public string Tag { get { return m_interface.Tag; } }
/// <summary>Short archive format description.</summary>
public string Description { get { return m_interface.Description; } }
/// <summary>Memory-mapped view of the archive.</summary>
public ArcView File { get { return m_arc; } }
/// <summary>Archive contents.</summary>
public ICollection<Entry> Dir { get { return m_dir; } }
public delegate ExtractAction ExtractCallback (int num, Entry entry);
public ArcFile (ArcView arc, ArchiveFormat impl, ICollection<Entry> dir)
{
m_arc = arc;
m_interface = impl;
m_dir = dir;
}
/// <summary>
/// Try to open <paramref name="filename"/> as archive.
/// Returns: ArcFile object if file is opened successfully, null otherwise.
/// </summary>
public static ArcFile TryOpen (string filename)
{
var file = new ArcView (filename);
try
{
uint signature = file.View.ReadUInt32 (0);
for (;;)
{
var range = FormatCatalog.Instance.LookupSignature<ArchiveFormat> (signature);
foreach (var impl in range)
{
try
{
var arc = impl.TryOpen (file);
if (null != arc)
{
file = null; // file ownership passed to ArcFile
return arc;
}
}
catch (Exception X)
{
// ignore failed open attmepts
Trace.WriteLine (string.Format ("[{0}] {1}: {2}", impl.Tag, filename, X.Message));
FormatCatalog.Instance.LastError = X;
}
}
if (0 == signature)
break;
signature = 0;
}
}
finally
{
if (null != file)
file.Dispose();
}
return null;
}
/// <summary>
/// Extract all entries from the archive into current directory.
/// <paramref name="callback"/> could be used to observe/control extraction process.
/// </summary>
public void ExtractFiles (ExtractCallback callback)
{
int i = 0;
foreach (var entry in Dir.OrderBy (e => e.Offset))
{
var action = callback (i, entry);
if (ExtractAction.Abort == action)
break;
if (ExtractAction.Skip != action)
Extract (entry);
++i;
}
}
/// <summary>
/// Extract specified <paramref name="entry"/> into current directory.
/// </summary>
public void Extract (Entry entry)
{
if (-1 != entry.Offset)
m_interface.Extract (this, entry);
}
/// <summary>
/// Open specified <paramref name="entry"/> as Stream.
/// </summary>
public Stream OpenEntry (Entry entry)
{
return m_interface.OpenEntry (this, entry);
}
/// <summary>
/// Create file corresponding to <paramref name="entry"/> within current directory and open
/// it for writing.
/// </summary>
public Stream CreateFile (Entry entry)
{
return m_interface.CreateFile (entry);
}
#region IDisposable Members
bool disposed = false;
public void Dispose ()
{
Dispose (true);
GC.SuppressFinalize (this);
}
protected virtual void Dispose (bool disposing)
{
if (!disposed)
{
if (disposing)
m_arc.Dispose();
m_arc = null;
disposed = true;
}
}
#endregion
}
public class AppendStream : System.IO.Stream
{
private Stream m_base;
private long m_start_pos;
public override bool CanRead { get { return true; } }
public override bool CanSeek { get { return true; } }
public override bool CanWrite { get { return true; } }
public override long Length { get { return m_base.Length - m_start_pos; } }
public override long Position
{
get { return m_base.Position - m_start_pos; }
set { m_base.Position = Math.Max (m_start_pos+value, m_start_pos); }
}
public AppendStream (System.IO.Stream file)
{
m_base = file;
m_start_pos = m_base.Seek (0, SeekOrigin.End);
}
public AppendStream (System.IO.Stream file, long offset)
{
m_base = file;
m_start_pos = m_base.Seek (offset, SeekOrigin.Begin);
}
public Stream BaseStream { get { return m_base; } }
public override void Flush()
{
m_base.Flush();
}
public override long Seek (long offset, SeekOrigin origin)
{
if (SeekOrigin.Begin == origin)
{
offset = Math.Max (offset + m_start_pos, m_start_pos);
}
long position = m_base.Seek (offset, origin);
if (position < m_start_pos)
{
m_base.Seek (m_start_pos, SeekOrigin.Begin);
position = m_start_pos;
}
return position - m_start_pos;
}
public override void SetLength (long length)
{
if (length < 0)
length = 0;
m_base.SetLength (length + m_start_pos);
}
public override int Read (byte[] buffer, int offset, int count)
{
return m_base.Read (buffer, offset, count);
}
public override int ReadByte ()
{
return m_base.ReadByte();
}
public override void Write (byte[] buffer, int offset, int count)
{
m_base.Write (buffer, offset, count);
}
public override void WriteByte (byte value)
{
m_base.WriteByte (value);
}
bool disposed = false;
protected override void Dispose (bool disposing)
{
if (!disposed)
{
m_base = null;
disposed = true;
base.Dispose (disposing);
}
}
}
}

470
GameRes/ArcView.cs Normal file
View File

@@ -0,0 +1,470 @@
//! \file ArcView.cs
//! \date Mon Jul 07 10:31:10 2014
//! \brief Memory mapped view of gameres file.
//
using System;
using System.IO;
using System.IO.MemoryMappedFiles;
using System.Runtime.InteropServices;
using System.Text;
namespace GameRes
{
public static class Encodings
{
public static readonly Encoding cp932 = Encoding.GetEncoding (932);
}
public static class StreamExtension
{
static public string ReadStringUntil (this Stream file, byte delim, Encoding enc)
{
byte[] buffer = new byte[16];
int size = 0;
for (;;)
{
int b = file.ReadByte ();
if (-1 == b || delim == b)
break;
if (buffer.Length == size)
{
byte[] new_buffer = new byte[checked(size/2*3)];
Array.Copy (buffer, new_buffer, size);
buffer = new_buffer;
}
buffer[size++] = (byte)b;
}
return enc.GetString (buffer, 0, size);
}
static public string ReadCString (this Stream file, Encoding enc)
{
return ReadStringUntil (file, 0, enc);
}
static public string ReadCString (this Stream file)
{
return ReadStringUntil (file, 0, Encodings.cp932);
}
}
public static class MappedViewExtension
{
static public string ReadString (this MemoryMappedViewAccessor view, long offset, uint size, Encoding enc)
{
byte[] buffer = new byte[size];
uint n;
for (n = 0; n < size; ++n)
{
byte b = view.ReadByte (offset+n);
if (0 == b)
break;
buffer[n] = b;
}
return enc.GetString (buffer, 0, (int)n);
}
static public string ReadString (this MemoryMappedViewAccessor view, long offset, uint size)
{
return ReadString (view, offset, size, Encodings.cp932);
}
unsafe public static byte* GetPointer (this MemoryMappedViewAccessor view, long offset)
{
var num = view.PointerOffset % info.dwAllocationGranularity;
byte* ptr = null;
view.SafeMemoryMappedViewHandle.AcquirePointer (ref ptr);
ptr += num;
return ptr;
}
[DllImport("kernel32.dll", SetLastError = true)]
internal static extern void GetSystemInfo (ref SYSTEM_INFO lpSystemInfo);
internal struct SYSTEM_INFO
{
internal int dwOemId;
internal int dwPageSize;
internal IntPtr lpMinimumApplicationAddress;
internal IntPtr lpMaximumApplicationAddress;
internal IntPtr dwActiveProcessorMask;
internal int dwNumberOfProcessors;
internal int dwProcessorType;
internal int dwAllocationGranularity;
internal short wProcessorLevel;
internal short wProcessorRevision;
}
static SYSTEM_INFO info;
static MappedViewExtension()
{
GetSystemInfo (ref info);
}
}
public class ArcView : IDisposable
{
private MemoryMappedFile m_map;
public const long PageSize = 4096;
public long MaxOffset { get; private set; }
public Frame View { get; private set; }
public ArcView (string name)
{
using (var fs = new FileStream(name, FileMode.Open, FileAccess.Read, FileShare.Read))
{
MaxOffset = fs.Length;
m_map = MemoryMappedFile.CreateFromFile (fs, null, 0,
MemoryMappedFileAccess.Read, null, HandleInheritability.None, false);
try {
View = new Frame (this);
} catch {
m_map.Dispose(); // dispose on error only
throw;
}
}
}
public Frame CreateFrame ()
{
return new Frame (View);
}
public ArcStream CreateStream ()
{
return new ArcStream (this);
}
public ArcStream CreateStream (long offset, uint size)
{
return new ArcStream (this, offset, size);
}
public MemoryMappedViewAccessor CreateViewAccessor (long offset, uint size)
{
return m_map.CreateViewAccessor (offset, size, MemoryMappedFileAccess.Read);
}
#region IDisposable Members
bool disposed = false;
public void Dispose ()
{
Dispose (true);
GC.SuppressFinalize (this);
}
protected virtual void Dispose (bool disposing)
{
if (!disposed)
{
if (disposing)
{
View.Dispose();
m_map.Dispose();
}
disposed = true;
m_map = null;
}
}
#endregion
public class Frame : IDisposable
{
private ArcView m_arc;
private MemoryMappedViewAccessor m_view;
private long m_offset;
private uint m_size;
public long Offset { get { return m_offset; } }
public uint Reserved { get { return m_size; } }
public Frame (ArcView arc)
{
m_arc = arc;
m_offset = 0;
m_size = (uint)Math.Min (ArcView.PageSize, m_arc.MaxOffset);
m_view = m_arc.CreateViewAccessor (m_offset, m_size);
}
public Frame (Frame other)
{
m_arc = other.m_arc;
m_offset = 0;
m_size = (uint)Math.Min (ArcView.PageSize, m_arc.MaxOffset);
m_view = m_arc.CreateViewAccessor (m_offset, m_size);
}
public Frame (ArcView arc, long offset, uint size)
{
m_arc = arc;
m_offset = Math.Min (offset, m_arc.MaxOffset);
m_size = (uint)Math.Min (size, m_arc.MaxOffset-m_offset);
m_view = m_arc.CreateViewAccessor (m_offset, m_size);
}
public uint Reserve (long offset, uint size)
{
if (offset < m_offset || offset+size > m_offset+m_size)
{
if (offset > m_arc.MaxOffset)
throw new ArgumentOutOfRangeException ("offset", "Too large offset specified for memory mapped file view.");
if (size < ArcView.PageSize)
size = (uint)ArcView.PageSize;
if (size > m_arc.MaxOffset-offset)
size = (uint)(m_arc.MaxOffset-offset);
m_view.Dispose();
m_view = m_arc.CreateViewAccessor (offset, size);
m_offset = offset;
m_size = size;
}
return (uint)(m_offset + m_size - offset);
}
public bool AsciiEqual (long offset, string data)
{
if (Reserve (offset, (uint)data.Length) < (uint)data.Length)
return false;
unsafe
{
byte* ptr = m_view.GetPointer (m_offset);
try {
for (int i = 0; i < data.Length; ++i)
{
if (ptr[offset-m_offset+i] != data[i])
return false;
}
} finally {
m_view.SafeMemoryMappedViewHandle.ReleasePointer();
}
return true;
}
}
public int Read (long offset, byte[] buf, int buf_offset, uint count)
{
// supposedly faster version of
//Reserve (offset, count);
//return m_view.ReadArray (offset-m_offset, buf, buf_offset, (int)count);
if (buf == null)
throw new ArgumentNullException ("buf", "Buffer cannot be null.");
if (buf_offset < 0)
throw new ArgumentOutOfRangeException ("buf_offset", "Buffer offset should be non-negative.");
int total = (int)Math.Min (Reserve (offset, count), count);
if (buf.Length - buf_offset < total)
throw new ArgumentException ("Buffer offset and length are out of bounds.");
unsafe
{
byte* ptr = m_view.GetPointer (m_offset);
try {
Marshal.Copy ((IntPtr)(ptr+(offset-m_offset)), buf, buf_offset, total);
} finally {
m_view.SafeMemoryMappedViewHandle.ReleasePointer();
}
}
return total;
}
public byte ReadByte (long offset)
{
Reserve (offset, 1);
return m_view.ReadByte (offset-m_offset);
}
public ushort ReadUInt16 (long offset)
{
Reserve (offset, 2);
return m_view.ReadUInt16 (offset-m_offset);
}
public short ReadInt16 (long offset)
{
Reserve (offset, 2);
return m_view.ReadInt16 (offset-m_offset);
}
public uint ReadUInt32 (long offset)
{
Reserve (offset, 4);
return m_view.ReadUInt32 (offset-m_offset);
}
public int ReadInt32 (long offset)
{
Reserve (offset, 4);
return m_view.ReadInt32 (offset-m_offset);
}
public ulong ReadUInt64 (long offset)
{
Reserve (offset, 8);
return m_view.ReadUInt64 (offset-m_offset);
}
public long ReadInt64 (long offset)
{
Reserve (offset, 8);
return m_view.ReadInt64 (offset-m_offset);
}
public string ReadString (long offset, uint size, Encoding enc)
{
Reserve (offset, size);
return m_view.ReadString (offset-m_offset, size, enc);
}
public string ReadString (long offset, uint size)
{
return ReadString (offset, size, Encodings.cp932);
}
#region IDisposable Members
bool disposed = false;
public void Dispose ()
{
Dispose (true);
GC.SuppressFinalize (this);
}
protected virtual void Dispose (bool disposing)
{
if (!disposed)
{
if (disposing)
{
m_view.Dispose();
}
m_arc = null;
m_view = null;
disposed = true;
}
}
#endregion
}
public class ArcStream : System.IO.Stream
{
private Frame m_view;
private long m_start;
private uint m_size;
private long m_position;
public override bool CanRead { get { return true; } }
public override bool CanSeek { get { return true; } }
public override bool CanWrite { get { return false; } }
public override long Length { get { return m_size; } }
public override long Position
{
get { return m_position; }
set { m_position = Math.Max (value, 0); }
}
public ArcStream (ArcView file)
{
m_view = file.CreateFrame();
m_start = 0;
m_size = (uint)Math.Min (file.MaxOffset, uint.MaxValue);
m_position = 0;
}
public ArcStream (ArcView file, long offset, uint size)
{
m_view = new Frame (file, offset, size);
m_start = m_view.Offset;
m_size = m_view.Reserved;
m_position = 0;
}
/// <summary>
/// Read stream signature (first 4 bytes) without altering current read position.
/// </summary>
public uint ReadSignature ()
{
return m_view.ReadUInt32 (m_start);
}
#region System.IO.Stream methods
public override void Flush()
{
}
public override long Seek (long offset, SeekOrigin origin)
{
switch (origin)
{
case SeekOrigin.Begin: m_position = offset; break;
case SeekOrigin.Current: m_position += offset; break;
case SeekOrigin.End: m_position = m_size + offset; break;
}
if (m_position < 0)
m_position = 0;
return m_position;
}
public override void SetLength (long length)
{
throw new NotSupportedException ("GameRes.ArcStream.SetLength method is not supported");
}
public override int Read (byte[] buffer, int offset, int count)
{
if (m_position >= m_size)
return 0;
count = (int)Math.Min (count, m_size - m_position);
int read = m_view.Read (m_start + m_position, buffer, offset, (uint)count);
m_position += read;
return read;
}
public override int ReadByte ()
{
if (m_position >= m_size)
return -1;
byte b = m_view.ReadByte (m_start+m_position);
++m_position;
return b;
}
public override void Write (byte[] buffer, int offset, int count)
{
throw new NotSupportedException("GameRes.ArcStream.Write method is not supported");
}
public override void WriteByte (byte value)
{
throw new NotSupportedException("GameRes.ArcStream.WriteByte method is not supported");
}
#endregion
#region IDisposable Members
bool disposed = false;
protected override void Dispose (bool disposing)
{
if (!disposed)
{
if (disposing)
{
m_view.Dispose();
}
disposed = true;
base.Dispose (disposing);
}
}
#endregion
}
public class Reader : System.IO.BinaryReader
{
public Reader (Stream stream) : base (stream, Encoding.ASCII, true)
{
}
}
}
}

347
GameRes/GameRes.cs Normal file
View File

@@ -0,0 +1,347 @@
//! \file GameRes.cs
//! \date Mon Jun 30 20:12:13 2014
//! \brief game resources browser.
//
using System;
using System.IO;
using System.Text;
using System.Linq;
using System.Collections.Generic;
using System.ComponentModel.Composition;
using System.ComponentModel.Composition.Hosting;
using System.Reflection;
using GameRes.Collections;
using GameRes.Strings;
namespace GameRes
{
/// <summary>
/// Basic filesystem entry.
/// </summary>
public class Entry
{
public virtual string Name { get; set; }
public virtual string Type { get; set; }
public long Offset { get; set; }
public uint Size { get; set; }
public Entry ()
{
Type = "";
Offset = -1;
}
/// <summary>
/// Check whether entry lies within specified file bound.
/// </summary>
public bool CheckPlacement (long max_offset)
{
return Offset < max_offset && Size < max_offset && Offset <= max_offset - Size;
}
}
public class PackedEntry : Entry
{
public uint UnpackedSize { get; set; }
public bool IsPacked { get; set; }
}
public abstract class IResource
{
/// <summary>Short tag sticked to resource (usually filename extension)</summary>
public abstract string Tag { get; }
/// <summary>Resource description (its source/inventor)</summary>
public abstract string Description { get; }
/// <summary>Resource type (image/archive/script)</summary>
public abstract string Type { get; }
/// <summary>First 4 bytes of the resource file as little-endian 32-bit integer,
/// or zero if it could vary.</summary>
public abstract uint Signature { get; }
/// <summary>Signatures peculiar to the resource (the one above is also included here).</summary>
public IEnumerable<uint> Signatures
{
get { return m_signatures; }
protected set { m_signatures = value; }
}
/// <summary>Filename extensions peculiar to the resource.</summary>
public IEnumerable<string> Extensions
{
get { return m_extensions; }
protected set { m_extensions = value; }
}
/// <summary>
/// Create empty Entry that corresponds to implemented resource.
/// </summary>
public virtual Entry CreateEntry ()
{
return new Entry { Type = this.Type };
}
private IEnumerable<string> m_extensions;
private IEnumerable<uint> m_signatures;
protected IResource ()
{
m_extensions = new string[] { Tag.ToLower() };
m_signatures = new uint[] { Signature };
}
}
public class ResourceOptions
{
public object Widget { get; set; }
}
public abstract class ArchiveFormat : IResource
{
public override string Type { get { return "archive"; } }
public abstract ArcFile TryOpen (ArcView view);
/// <summary>
/// Extract file referenced by <paramref name="entry"/> into current directory.
/// </summary>
public virtual void Extract (ArcFile file, Entry entry)
{
using (var reader = OpenEntry (file, entry))
{
using (var writer = CreateFile (entry))
{
reader.CopyTo (writer);
}
}
}
/// <summary>
/// Open file referenced by <paramref name="entry"/> as Stream.
/// </summary>
public virtual Stream OpenEntry (ArcFile arc, Entry entry)
{
return arc.File.CreateStream (entry.Offset, entry.Size);
}
/// <summary>
/// Create file corresponding to <paramref name="entry"/> in current directory and open it
/// for writing.
/// </summary>
public virtual Stream CreateFile (Entry entry)
{
string dir = Path.GetDirectoryName (entry.Name);
if (!string.IsNullOrEmpty (dir))
{
Directory.CreateDirectory (dir);
}
return File.Create (entry.Name);
}
/// /// <summary>
/// Create resource archive named <paramref name="filename"/> containing entries from the
/// supplied <paramref name="list"/> and applying necessary <paramref name="options"/>.
/// </summary>
public virtual void Create (string filename, IEnumerable<Entry> list, ResourceOptions options = null)
{
throw new NotImplementedException ("ArchiveFormat.Create is not implemented");
}
public virtual ResourceOptions GetOptions ()
{
return null;
}
}
public delegate void ParametersRequestEventHandler (object sender, ParametersRequestEventArgs e);
public class ParametersRequestEventArgs : EventArgs
{
/// <summary>
/// String describing request nature (encryption key etc).
/// </summary>
public string Notice { get; set; }
/// <summary>
/// UIElement responsible for displaying request.
/// </summary>
public object InputWidget { get; set; }
/// <summary>
/// Return value from ShowDialog()
/// </summary>
public bool InputResult { get; set; }
}
public sealed class FormatCatalog
{
private static readonly FormatCatalog m_instance = new FormatCatalog();
#pragma warning disable 649
[ImportMany(typeof(ArchiveFormat))]
private IEnumerable<ArchiveFormat> m_arc_formats;
[ImportMany(typeof(ImageFormat))]
private IEnumerable<ImageFormat> m_image_formats;
[ImportMany(typeof(ScriptFormat))]
private IEnumerable<ScriptFormat> m_script_formats;
#pragma warning restore 649
private MultiValueDictionary<string, IResource> m_extension_map = new MultiValueDictionary<string, IResource>();
private MultiValueDictionary<uint, IResource> m_signature_map = new MultiValueDictionary<uint, IResource>();
/// <summary> The only instance of this class.</summary>
public static FormatCatalog Instance { get { return m_instance; } }
public IEnumerable<ArchiveFormat> ArcFormats { get { return m_arc_formats; } }
public IEnumerable<ImageFormat> ImageFormats { get { return m_image_formats; } }
public IEnumerable<ScriptFormat> ScriptFormats { get { return m_script_formats; } }
public Exception LastError { get; set; }
public event ParametersRequestEventHandler ParametersRequest;
private FormatCatalog ()
{
//An aggregate catalog that combines multiple catalogs
var catalog = new AggregateCatalog();
//Adds all the parts found in the same assembly as the Program class
catalog.Catalogs.Add (new AssemblyCatalog (typeof(FormatCatalog).Assembly));
//Adds parts matching pattern found in the directory of the assembly
catalog.Catalogs.Add (new DirectoryCatalog (Path.GetDirectoryName (System.Reflection.Assembly.GetExecutingAssembly().Location), "Arc*.dll"));
//Create the CompositionContainer with the parts in the catalog
var container = new CompositionContainer (catalog);
//Fill the imports of this object
container.ComposeParts (this);
AddResourceImpl (m_arc_formats);
AddResourceImpl (m_image_formats);
AddResourceImpl (m_script_formats);
}
private void AddResourceImpl (IEnumerable<IResource> formats)
{
foreach (var impl in formats)
{
foreach (var ext in impl.Extensions)
{
m_extension_map.Add (ext.ToUpper(), impl);
}
foreach (var signature in impl.Signatures)
{
m_signature_map.Add (signature, impl);
}
}
}
/// <summary>
/// Look up filename in format registry by filename extension and return corresponding interfaces.
/// if no formats available, return empty range.
/// </summary>
public IEnumerable<IResource> LookupFileName (string filename)
{
string ext = Path.GetExtension (filename);
if (null == ext)
return new IResource[0];
return LookupTag (ext.TrimStart ('.'));
}
public IEnumerable<IResource> LookupTag (string tag)
{
return m_extension_map.GetValues (tag.ToUpper(), true);
}
public IEnumerable<Type> LookupTag<Type> (string tag) where Type : IResource
{
return LookupTag (tag).OfType<Type>();
}
public IEnumerable<IResource> LookupSignature (uint signature)
{
return m_signature_map.GetValues (signature, true);
}
public IEnumerable<Type> LookupSignature<Type> (uint signature) where Type : IResource
{
return LookupSignature (signature).OfType<Type>();
}
/// <summary>
/// Create GameRes.Entry corresponding to <paramref name="filename"/> extension.
/// </summary>
public Entry CreateEntry (string filename)
{
Entry entry = null;
string ext = Path.GetExtension (filename);
if (null != ext)
{
ext = ext.TrimStart ('.').ToUpper();
var range = m_extension_map.GetValues (ext, false);
if (null != range)
entry = range.First().CreateEntry();
}
if (null == entry)
entry = new Entry();
entry.Name = filename;
return entry;
}
public string GetTypeFromName (string filename)
{
var formats = LookupFileName (filename);
if (formats.Any())
return formats.First().Type;
return "";
}
public void InvokeParametersRequest (object source, ParametersRequestEventArgs args)
{
if (null != ParametersRequest)
ParametersRequest (source, args);
}
/// <summary>
/// Read first 4 bytes from stream and return them as 32-bit signature.
/// </summary>
public static uint ReadSignature (Stream file)
{
file.Position = 0;
uint signature = (byte)file.ReadByte();
if (BitConverter.IsLittleEndian)
{
signature |= (uint)file.ReadByte() << 8;
signature |= (uint)file.ReadByte() << 16;
signature |= (uint)file.ReadByte() << 24;
}
else
{
signature <<= 24;
signature |= (uint)file.ReadByte() << 16;
signature |= (uint)file.ReadByte() << 8;
signature |= (byte)file.ReadByte();
}
return signature;
}
}
public class InvalidFormatException : Exception
{
public InvalidFormatException() : base(garStrings.MsgInvalidFormat) { }
public InvalidFormatException (string msg) : base (msg) { }
}
public class UnknownEncryptionScheme : Exception
{
public UnknownEncryptionScheme() : base(garStrings.MsgUnknownEncryption) { }
public UnknownEncryptionScheme (string msg) : base (msg) { }
}
public class InvalidEncryptionScheme : Exception
{
public InvalidEncryptionScheme() : base(garStrings.MsgInvalidEncryption) { }
public InvalidEncryptionScheme (string msg) : base (msg) { }
}
}

89
GameRes/GameRes.csproj Normal file
View File

@@ -0,0 +1,89 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="12.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{453C087F-E416-4AE9-8C03-D8760DA0574B}</ProjectGuid>
<OutputType>Library</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>GameRes</RootNamespace>
<AssemblyName>GameRes</AssemblyName>
<TargetFrameworkVersion>v4.5.1</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<TargetFrameworkProfile />
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>..\bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<Prefer32Bit>false</Prefer32Bit>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>..\bin\Release\</OutputPath>
<DefineConstants>
</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<Prefer32Bit>false</Prefer32Bit>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup>
<ItemGroup>
<Reference Include="PresentationCore" />
<Reference Include="System" />
<Reference Include="System.ComponentModel.Composition" />
<Reference Include="System.Core" />
<Reference Include="System.Xaml" />
<Reference Include="System.Xml.Linq" />
<Reference Include="System.Data.DataSetExtensions" />
<Reference Include="Microsoft.CSharp" />
<Reference Include="System.Data" />
<Reference Include="System.Xml" />
<Reference Include="WindowsBase" />
</ItemGroup>
<ItemGroup>
<Compile Include="ArcFile.cs" />
<Compile Include="ArcView.cs" />
<Compile Include="GameRes.cs" />
<Compile Include="Image.cs" />
<Compile Include="ImageBMP.cs" />
<Compile Include="ImageJPEG.cs" />
<Compile Include="ImagePNG.cs" />
<Compile Include="ImageTGA.cs" />
<Compile Include="ImageTIFF.cs" />
<Compile Include="MultiDict.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="ScriptText.cs" />
<Compile Include="Strings\garStrings.Designer.cs">
<AutoGen>True</AutoGen>
<DesignTime>True</DesignTime>
<DependentUpon>garStrings.resx</DependentUpon>
</Compile>
<Compile Include="Utility.cs" />
</ItemGroup>
<ItemGroup>
<WCFMetadata Include="Service References\" />
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="Strings\garStrings.resx">
<Generator>PublicResXFileCodeGenerator</Generator>
<LastGenOutput>garStrings.Designer.cs</LastGenOutput>
</EmbeddedResource>
<EmbeddedResource Include="Strings\garStrings.ru-RU.resx" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.
<Target Name="BeforeBuild">
</Target>
<Target Name="AfterBuild">
</Target>
-->
</Project>

100
GameRes/Image.cs Normal file
View File

@@ -0,0 +1,100 @@
//! \file Image.cs
//! \date Tue Jul 01 11:29:52 2014
//! \brief image class.
//
using System.IO;
using System.Windows.Media.Imaging;
namespace GameRes
{
public class ImageMetaData
{
public uint Width { get; set; }
public uint Height { get; set; }
public int OffsetX { get; set; }
public int OffsetY { get; set; }
public int BPP { get; set; }
}
public class ImageEntry : Entry
{
public override string Type { get { return "image"; } }
/*
public ImageEntry ()
{
Type = "image";
}
*/
}
public class ImageData
{
private BitmapSource m_bitmap;
public BitmapSource Bitmap { get { return m_bitmap; } }
public uint Width { get { return (uint)m_bitmap.PixelWidth; } }
public uint Height { get { return (uint)m_bitmap.PixelHeight; } }
public int OffsetX { get; set; }
public int OffsetY { get; set; }
public int BPP { get { return m_bitmap.Format.BitsPerPixel; } }
public ImageData (BitmapSource data, ImageMetaData meta)
{
m_bitmap = data;
OffsetX = meta.OffsetX;
OffsetY = meta.OffsetY;
}
public ImageData (BitmapSource data, int x = 0, int y = 0)
{
m_bitmap = data;
OffsetX = x;
OffsetY = y;
}
}
public abstract class ImageFormat : IResource
{
public override string Type { get { return "image"; } }
public ImageData Read (Stream file)
{
bool need_dispose = false;
try
{
if (!file.CanSeek)
{
var stream = new MemoryStream();
file.CopyTo (stream);
file = stream;
need_dispose = true;
}
var info = ReadMetaData (file);
if (null == info)
throw new InvalidFormatException();
return Read (file, info);
}
finally
{
if (need_dispose)
file.Dispose();
}
}
public abstract ImageData Read (Stream file, ImageMetaData info);
public abstract void Write (Stream file, ImageData bitmap);
public abstract ImageMetaData ReadMetaData (Stream file);
public override Entry CreateEntry ()
{
return new ImageEntry();
}
public bool IsBuiltin
{
get { return this.GetType().Assembly == typeof(ImageFormat).Assembly; }
}
}
}

81
GameRes/ImageBMP.cs Normal file
View File

@@ -0,0 +1,81 @@
//! \file ImageBMP.cs
//! \date Wed Jul 16 18:06:47 2014
//! \brief BMP image implementation.
//
using System.IO;
using System.Linq;
using System.Text;
using System.ComponentModel.Composition;
using System.Windows.Media.Imaging;
using GameRes.Utility;
using System.Windows.Media;
namespace GameRes
{
[Export(typeof(ImageFormat))]
public class BmpFormat : ImageFormat
{
public override string Tag { get { return "BMP"; } }
public override string Description { get { return "Windows device independent bitmap"; } }
public override uint Signature { get { return 0; } }
public override ImageData Read (Stream file, ImageMetaData info)
{
var decoder = new BmpBitmapDecoder (file,
BitmapCreateOptions.None, BitmapCacheOption.OnLoad);
BitmapSource frame = decoder.Frames.First();
frame.Freeze();
return new ImageData (frame, info);
}
public override void Write (Stream file, ImageData image)
{
var encoder = new BmpBitmapEncoder();
encoder.Frames.Add (BitmapFrame.Create (image.Bitmap));
encoder.Save (file);
}
void SkipBytes (BinaryReader file, uint num)
{
if (file.BaseStream.CanSeek)
file.BaseStream.Seek (num, SeekOrigin.Current);
else
{
for (int i = 0; i < num / 4; ++i)
file.ReadInt32();
for (int i = 0; i < num % 4; ++i)
file.ReadByte();
}
}
public override ImageMetaData ReadMetaData (Stream stream)
{
int c1 = stream.ReadByte();
int c2 = stream.ReadByte();
if (0x42 != c1 || 0x4d != c2)
return null;
using (var file = new ArcView.Reader (stream))
{
uint size = file.ReadUInt32();
if (size < 14+40)
return null;
SkipBytes (file, 8);
uint header_size = file.ReadUInt32();
if (header_size < 40 || size-14 < header_size)
return null;
uint width = file.ReadUInt32();
uint height = file.ReadUInt32();
file.ReadInt16();
int bpp = file.ReadInt16();
return new ImageMetaData {
Width = width,
Height = height,
OffsetX = 0,
OffsetY = 0,
BPP = bpp
};
}
}
}
}

79
GameRes/ImageJPEG.cs Normal file
View File

@@ -0,0 +1,79 @@
//! \file ImageJPEG.cs
//! \date Thu Jul 17 15:56:27 2014
//! \brief JPEG image implementation.
//
using System;
using System.IO;
using System.Text;
using System.ComponentModel.Composition;
using System.Windows.Media.Imaging;
using GameRes.Utility;
namespace GameRes
{
[Export(typeof(ImageFormat))]
public class JpegFormat : ImageFormat
{
public override string Tag { get { return "JPEG"; } }
public override string Description { get { return "JPEG image file format"; } }
public override uint Signature { get { return 0; } }
public int Quality { get; set; }
public JpegFormat ()
{
Extensions = new string[] { "jpg", "jpeg" };
Quality = 90;
}
public override ImageData Read (Stream file, ImageMetaData info)
{
var decoder = new JpegBitmapDecoder (file,
BitmapCreateOptions.None, BitmapCacheOption.OnLoad);
var frame = decoder.Frames[0];
frame.Freeze();
return new ImageData (frame, info);
}
public override void Write (Stream file, ImageData image)
{
var encoder = new JpegBitmapEncoder();
encoder.QualityLevel = Quality;
encoder.Frames.Add (BitmapFrame.Create (image.Bitmap));
encoder.Save (file);
}
public override ImageMetaData ReadMetaData (Stream stream)
{
if (0xff != stream.ReadByte() || 0xd8 != stream.ReadByte())
return null;
using (var file = new ArcView.Reader (stream))
{
while (-1 != file.PeekChar())
{
ushort marker = Binary.BigEndian (file.ReadUInt16());
if ((marker & 0xff00) != 0xff00)
break;
int length = Binary.BigEndian (file.ReadUInt16());
if ((marker & 0x00f0) == 0xc0 && marker != 0xffc4)
{
if (length < 8)
break;
int bits = file.ReadByte();
uint height = Binary.BigEndian (file.ReadUInt16());
uint width = Binary.BigEndian (file.ReadUInt16());
int components = file.ReadByte();
return new ImageMetaData {
Width = width,
Height = height,
BPP = bits * components,
};
}
file.BaseStream.Seek (length-2, SeekOrigin.Current);
}
return null;
}
}
}
}

195
GameRes/ImagePNG.cs Normal file
View File

@@ -0,0 +1,195 @@
//! \file ImagePNG.cs
//! \date Sat Jul 05 00:09:15 2014
//! \brief PNG image implementation.
//
using System.IO;
using System.Linq;
using System.Text;
using System.ComponentModel.Composition;
using System.Windows.Media.Imaging;
using GameRes.Utility;
using System.Windows.Media;
namespace GameRes
{
[Export(typeof(ImageFormat))]
public class PngFormat : ImageFormat
{
public override string Tag { get { return "PNG"; } }
public override string Description { get { return "Portable Network Graphics image"; } }
public override uint Signature { get { return 0x474e5089; } }
public override ImageData Read (Stream file, ImageMetaData info)
{
var decoder = new PngBitmapDecoder (file,
BitmapCreateOptions.None, BitmapCacheOption.OnLoad);
BitmapSource frame = decoder.Frames.First();
frame.Freeze();
return new ImageData (frame, info);
}
public override void Write (Stream file, ImageData image)
{
var encoder = new PngBitmapEncoder();
encoder.Frames.Add (BitmapFrame.Create (image.Bitmap));
if (0 == image.OffsetX && 0 == image.OffsetY)
{
encoder.Save (file);
return;
}
using (var mem_stream = new MemoryStream())
{
encoder.Save (mem_stream);
byte[] buf = mem_stream.GetBuffer();
long header_pos = 8;
mem_stream.Position = header_pos;
uint header_length = ReadChunkLength (mem_stream);
file.Write (buf, 0, (int)(header_pos+header_length+12));
WriteOffsChunk (file, image);
mem_stream.Position = header_pos+header_length+12;
mem_stream.CopyTo (file);
}
}
uint ReadChunkLength (Stream file)
{
int length = file.ReadByte() << 24;
length |= file.ReadByte() << 16;
length |= file.ReadByte() << 8;
length |= file.ReadByte();
return (uint)length;
}
void WriteOffsChunk (Stream file, ImageData image)
{
using (var membuf = new MemoryStream (32))
{
using (var bin = new BinaryWriter (membuf, Encoding.ASCII, true))
{
bin.Write (Binary.BigEndian ((uint)9));
char[] tag = { 'o', 'F', 'F', 's' };
bin.Write (tag);
bin.Write (Binary.BigEndian ((uint)image.OffsetX));
bin.Write (Binary.BigEndian ((uint)image.OffsetY));
bin.Write ((byte)0);
bin.Flush();
uint crc = Crc32.Compute (membuf.GetBuffer(), 8, 9);
bin.Write (Binary.BigEndian (crc));
}
file.Write (membuf.GetBuffer(), 0, 9+12); // chunk + size+id+crc
}
}
void SkipBytes (BinaryReader file, uint num)
{
if (file.BaseStream.CanSeek)
file.BaseStream.Seek (num, SeekOrigin.Current);
else
{
for (int i = 0; i < num / 4; ++i)
file.ReadInt32();
for (int i = 0; i < num % 4; ++i)
file.ReadByte();
}
}
public override ImageMetaData ReadMetaData (Stream stream)
{
ImageMetaData meta = null;
var file = new ArcView.Reader (stream);
try
{
if (file.ReadUInt32() != Signature)
return null;
if (file.ReadUInt32() != 0x0a1a0a0d)
return null;
uint chunk_size = Binary.BigEndian (file.ReadUInt32());
char[] chunk_type = new char[4];
file.Read (chunk_type, 0, 4);
if (!chunk_type.SequenceEqual ("IHDR"))
return null;
meta = new ImageMetaData();
meta.Width = Binary.BigEndian (file.ReadUInt32());
meta.Height = Binary.BigEndian (file.ReadUInt32());
int bpp = file.ReadByte();
int color_type = file.ReadByte();
switch (color_type)
{
case 2: meta.BPP = bpp*3; break;
case 3: meta.BPP = 24; break;
case 4: meta.BPP = bpp*2; break;
case 6: meta.BPP = bpp*4; break;
default: meta.BPP = bpp; break;
}
SkipBytes (file, 7);
for (;;)
{
chunk_size = Binary.BigEndian (file.ReadUInt32());
file.Read (chunk_type, 0, 4);
if (chunk_type.SequenceEqual ("IDAT") || chunk_type.SequenceEqual ("IEND"))
break;
if (chunk_type.SequenceEqual ("oFFs"))
{
int x = Binary.BigEndian (file.ReadInt32());
int y = Binary.BigEndian (file.ReadInt32());
if (0 == file.ReadByte())
{
meta.OffsetX = x;
meta.OffsetY = y;
}
break;
}
SkipBytes (file, chunk_size+4);
}
}
catch
{
meta = null;
}
finally
{
file.Dispose();
if (stream.CanSeek)
stream.Position = 0;
}
return meta;
}
long FindChunk (Stream stream, string chunk)
{
char[] find_name = chunk.ToCharArray();
long found_offset = -1;
var file = new ArcView.Reader (stream);
try
{
char[] buf = new char[4];
file.BaseStream.Position = 8;
while (-1 != file.PeekChar())
{
long chunk_offset = file.BaseStream.Position;
uint chunk_size = Binary.BigEndian (file.ReadUInt32());
if (4 != file.Read (buf, 0, 4))
break;
if (chunk.SequenceEqual (buf))
{
found_offset = chunk_offset;
break;
}
file.BaseStream.Position += chunk_size + 4;
}
}
catch
{
// ignore errors
}
finally
{
file.Dispose();
}
return found_offset;
}
}
}

204
GameRes/ImageTGA.cs Normal file
View File

@@ -0,0 +1,204 @@
//! \file ImageTGA.cs
//! \date Fri Jul 04 07:24:38 2014
//! \brief Targa image implementation.
//
using System.IO;
using System.Text;
using System.Windows;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.ComponentModel.Composition;
namespace GameRes
{
public class TgaMetaData : ImageMetaData
{
public short ImageType;
public short ColormapType;
public uint ColormapOffset;
public ushort ColormapFirst;
public ushort ColormapLength;
public short ColormapDepth;
public short Descriptor;
}
[Export(typeof(ImageFormat))]
public class TgaFormat : ImageFormat
{
public override string Tag { get { return "TGA"; } }
public override string Description { get { return "Truevision TGA image"; } }
public override uint Signature { get { return 0; } }
public override ImageData Read (Stream stream, ImageMetaData metadata)
{
var meta = metadata as TgaMetaData;
if (null == meta)
throw new System.ArgumentException ("TgaFormat.Read should be supplied with TgaMetaData", "metadata");
int colormap_size = meta.ColormapLength * meta.ColormapDepth / 8;
int width = (int)meta.Width;
int height = (int)meta.Height;
int bpp = meta.BPP;
long image_offset = meta.ColormapOffset;
if (1 == meta.ColormapType)
image_offset += colormap_size;
switch (meta.ImageType)
{
case 1: // Uncompressed, color-mapped images.
case 3: // Uncompressed, black and white images.
case 9: // Runlength encoded color-mapped images.
case 10: // Runlength encoded RGB images.
case 11: // Compressed, black and white images.
case 32: // Compressed color-mapped data, using Huffman, Delta, and
// runlength encoding.
case 33: // Compressed color-mapped data, using Huffman, Delta, and
// runlength encoding. 4-pass quadtree-type process.
throw new System.NotImplementedException();
default:
throw new InvalidFormatException();
case 2: // Uncompressed, RGB images.
{
PixelFormat pixel_format;
switch (bpp)
{
default: throw new InvalidFormatException();
case 24: pixel_format = PixelFormats.Bgr24; break;
case 32: pixel_format = PixelFormats.Bgra32; break;
case 15: pixel_format = PixelFormats.Bgr555; break;
case 16: pixel_format = PixelFormats.Bgr565; break;
}
stream.Position = image_offset;
int stride = width*((bpp+7)/8);
byte[] data = new byte[stride*height];
if (0 != (meta.Descriptor & 0x20))
{
if (data.Length != stream.Read (data, 0, data.Length))
throw new InvalidFormatException();
}
else
{
for (int row = height-1; row >= 0; --row)
{
if (stride != stream.Read (data, row*stride, stride))
throw new InvalidFormatException();
}
}
var bitmap = BitmapSource.Create (width, height, 96, 96, pixel_format, null,
data, stride);
bitmap.Freeze();
return new ImageData (bitmap, meta);
}
throw new InvalidFormatException();
}
}
public override void Write (Stream stream, ImageData image)
{
using (var file = new BinaryWriter (stream, System.Text.Encoding.ASCII, true))
{
file.Write ((byte)0); // idlength
file.Write ((byte)0); // colourmaptype
file.Write ((byte)2); // datatypecode
file.Write ((short)0); // colourmaporigin
file.Write ((short)0); // colourmaplength
file.Write ((byte)0); // colourmapdepth
file.Write ((short)image.OffsetX);
file.Write ((short)image.OffsetY);
file.Write ((ushort)image.Width);
file.Write ((ushort)image.Height);
var bitmap = image.Bitmap;
int bpp = 0;
int stride = 0;
byte descriptor = 0;
if (PixelFormats.Bgr24 == bitmap.Format)
{
bpp = 24;
stride = (int)image.Width*3;
}
else if (PixelFormats.Bgr32 == bitmap.Format)
{
bpp = 32;
stride = (int)image.Width*4;
}
else
{
bpp = 32;
stride = (int)image.Width*4;
if (PixelFormats.Bgra32 != bitmap.Format)
{
var converted_bitmap = new FormatConvertedBitmap();
converted_bitmap.BeginInit();
converted_bitmap.Source = image.Bitmap;
converted_bitmap.DestinationFormat = PixelFormats.Bgra32;
converted_bitmap.EndInit();
bitmap = converted_bitmap;
}
}
file.Write ((byte)bpp);
file.Write (descriptor);
byte[] row_data = new byte[stride];
Int32Rect rect = new Int32Rect (0, (int)image.Height, (int)image.Width, 1);
for (uint row = 0; row < image.Height; ++row)
{
--rect.Y;
bitmap.CopyPixels (rect, row_data, stride, 0);
file.Write (row_data);
}
}
}
public override ImageMetaData ReadMetaData (Stream stream)
{
using (var file = new ArcView.Reader (stream))
{
short id_length = file.ReadByte();
short colormap_type = file.ReadByte();
if (colormap_type > 1)
return null;
short image_type = file.ReadByte();
ushort colormap_first = file.ReadUInt16();
ushort colormap_length = file.ReadUInt16();
short colormap_depth = file.ReadByte();
int pos_x = file.ReadInt16();
int pos_y = file.ReadInt16();
uint width = file.ReadUInt16();
uint height = file.ReadUInt16();
int bpp = file.ReadByte();
if (bpp != 32 && bpp != 24 && bpp != 16 && bpp != 15 && bpp != 8)
return null;
short descriptor = file.ReadByte();
uint colormap_offset = (uint)(18 + id_length);
switch (image_type)
{
default: return null;
case 1: // Uncompressed, color-mapped images.
case 2: // Uncompressed, RGB images.
case 3: // Uncompressed, black and white images.
case 9: // Runlength encoded color-mapped images.
case 10: // Runlength encoded RGB images.
case 11: // Compressed, black and white images.
case 32: // Compressed color-mapped data, using Huffman, Delta, and
// runlength encoding.
case 33: // Compressed color-mapped data, using Huffman, Delta, and
// runlength encoding. 4-pass quadtree-type process.
break;
}
return new TgaMetaData {
OffsetX = pos_x,
OffsetY = pos_y,
Width = width,
Height = height,
BPP = bpp,
ImageType = image_type,
ColormapType = colormap_type,
ColormapOffset = colormap_offset,
ColormapFirst = colormap_first,
ColormapLength = colormap_length,
ColormapDepth = colormap_depth,
Descriptor = descriptor,
};
}
}
}
}

363
GameRes/ImageTIFF.cs Normal file
View File

@@ -0,0 +1,363 @@
//! \file ImageTIFF.cs
//! \date Mon Jul 07 06:39:45 2014
//! \brief TIFF image implementation.
//
using System;
using System.IO;
using System.Text;
using System.ComponentModel.Composition;
using System.Windows.Media.Imaging;
using GameRes.Utility;
namespace GameRes
{
#if DEBUG
// FIXME
// .Net TIFF encoder messes up transparency,
// therefore this class is disabled in release build
[Export(typeof(ImageFormat))]
#endif
public class TifFormat : ImageFormat
{
public override string Tag { get { return "TIFF"; } }
public override string Description { get { return "Tagged Image File Format"; } }
public override uint Signature { get { return 0; } }
public TifFormat ()
{
Extensions = new string[] { "tif", "tiff" };
Signatures = new uint[] { 0x002a4949, 0x2a004d4d };
}
public override ImageData Read (Stream file, ImageMetaData info)
{
var decoder = new TiffBitmapDecoder (file,
BitmapCreateOptions.PreservePixelFormat, BitmapCacheOption.OnLoad);
var frame = decoder.Frames[0];
frame.Freeze();
return new ImageData (frame, info);
}
public override void Write (Stream file, ImageData image)
{
var encoder = new TiffBitmapEncoder();
encoder.Compression = TiffCompressOption.Zip;
encoder.Frames.Add (BitmapFrame.Create (image.Bitmap));
encoder.Save (file);
}
private delegate uint UInt32Reader();
private delegate ushort UInt16Reader();
enum TIFF
{
ImageWidth = 0x100,
ImageHeight = 0x101,
BitsPerSample = 0x102,
Compression = 0x103,
SamplesPerPixel = 0x115,
XResolution = 0x11a,
YResolution = 0x11b,
XPosition = 0x11e,
YPosition = 0x11f,
}
enum TagType
{
Byte = 1,
Ascii = 2,
Short = 3,
Long = 4,
Rational = 5,
SByte = 6,
Undefined = 7,
SShort = 8,
SLong = 9,
SRational = 10,
Float = 11,
Double = 12,
LastKnown = Double,
}
enum MetaParsed
{
None = 0,
Width = 1,
Height = 2,
BPP = 4,
PosX = 8,
PosY = 16,
Sufficient = Width|Height|BPP,
Complete = Sufficient|PosX|PosY,
}
public override ImageMetaData ReadMetaData (Stream stream)
{
using (var file = new Parser (stream))
return file.ReadMetaData();
}
public class Parser : IDisposable
{
private BinaryReader m_file;
private readonly bool m_is_bigendian;
private readonly uint m_first_ifd;
private readonly uint[] m_type_size = { 0, 1, 1, 2, 4, 8, 1, 1, 2, 4, 8, 4, 8 };
private delegate ushort UInt16Reader ();
private delegate uint UInt32Reader ();
private delegate ulong UInt64Reader ();
UInt16Reader ReadUInt16;
UInt32Reader ReadUInt32;
UInt64Reader ReadUInt64;
public Parser (Stream file)
{
m_file = new ArcView.Reader (file);
uint signature = m_file.ReadUInt32();
m_is_bigendian = 0x2a004d4d == signature;
if (m_is_bigendian)
{
ReadUInt16 = () => Binary.BigEndian (m_file.ReadUInt16());
ReadUInt32 = () => Binary.BigEndian (m_file.ReadUInt32());
ReadUInt64 = () => Binary.BigEndian (m_file.ReadUInt64());
}
else
{
ReadUInt16 = () => m_file.ReadUInt16();
ReadUInt32 = () => m_file.ReadUInt32();
ReadUInt64 = () => m_file.ReadUInt64();
}
m_first_ifd = ReadUInt32();
}
public long FindLastIFD ()
{
uint ifd = m_first_ifd;
for (;;)
{
m_file.BaseStream.Position = ifd;
uint tag_count = ReadUInt16();
ifd += 2 + tag_count*12;
uint ifd_next = ReadUInt32();
if (0 == ifd_next)
break;
if (ifd_next == ifd || ifd_next >= m_file.BaseStream.Length)
return -1;
ifd = ifd_next;
}
return ifd;
}
public ImageMetaData ReadMetaData ()
{
MetaParsed parsed = MetaParsed.None;
int width = 0, height = 0, bpp = 0, pos_x = 0, pos_y = 0;
uint ifd = m_first_ifd;
while (ifd != 0 && parsed != MetaParsed.Complete)
{
m_file.BaseStream.Position = ifd;
uint tag_count = ReadUInt16();
ifd += 2;
for (uint i = 0; i < tag_count && parsed != MetaParsed.Complete; ++i)
{
ushort tag = ReadUInt16();
TagType type = (TagType)ReadUInt16();
uint count = ReadUInt32();
if (0 != count && 0 != type && type <= TagType.LastKnown)
{
switch ((TIFF)tag)
{
case TIFF.ImageWidth:
if (1 == count)
if (ReadOffsetValue (type, out width))
parsed |= MetaParsed.Width;
break;
case TIFF.ImageHeight:
if (1 == count)
if (ReadOffsetValue (type, out height))
parsed |= MetaParsed.Height;
break;
case TIFF.XPosition:
if (1 == count)
if (ReadOffsetValue (type, out pos_x))
parsed |= MetaParsed.PosX;
break;
case TIFF.YPosition:
if (1 == count)
if (ReadOffsetValue (type, out pos_y))
parsed |= MetaParsed.PosY;
break;
case TIFF.BitsPerSample:
if (count * GetTypeSize (type) > 4)
{
var bpp_offset = ReadUInt32();
m_file.BaseStream.Position = bpp_offset;
}
bpp = 0;
for (uint b = 0; b < count; ++b)
{
int plane = 0;
ReadValue (type, out plane);
bpp += plane;
}
parsed |= MetaParsed.BPP;
break;
default:
break;
}
}
ifd += 12;
m_file.BaseStream.Position = ifd;
}
uint ifd_next = ReadUInt32();
if (ifd_next == ifd)
break;
ifd = ifd_next;
}
if (MetaParsed.Sufficient == (parsed & MetaParsed.Sufficient))
return new ImageMetaData() {
Width = (uint)width,
Height = (uint)height,
OffsetX = pos_x,
OffsetY = pos_y,
BPP = bpp,
};
else
return null;
}
uint GetTypeSize (TagType type)
{
if ((int)type < m_type_size.Length)
return m_type_size[(int)type];
else
return 0;
}
bool ReadOffsetValue (TagType type, out int value)
{
if (GetTypeSize (type) > 4)
m_file.BaseStream.Position = ReadUInt32();
return ReadValue (type, out value);
}
bool ReadValue (TagType type, out int value)
{
switch (type)
{
case TagType.Undefined:
case TagType.SByte:
case TagType.Byte:
value = m_file.ReadByte();
break;
default:
case TagType.Ascii:
value = 0;
return false;
case TagType.SShort:
case TagType.Short:
value = ReadUInt16();
break;
case TagType.SLong:
case TagType.Long:
value = (int)ReadUInt32();
break;
case TagType.Rational:
return ReadRational (out value);
case TagType.SRational:
return ReadSRational (out value);
case TagType.Float:
return ReadFloat (out value);
case TagType.Double:
return ReadDouble (out value);
}
return true;
}
bool ReadRational (out int value)
{
uint numer = ReadUInt32();
uint denom = ReadUInt32();
if (1 == denom)
value = (int)numer;
else if (0 == denom)
{
value = 0;
return false;
}
else
value = (int)((double)numer / denom);
return true;
}
bool ReadSRational (out int value)
{
int numer = (int)ReadUInt32();
int denom = (int)ReadUInt32();
if (1 == denom)
value = numer;
else if (0 == denom)
{
value = 0;
return false;
}
else
value = (int)((double)numer / denom);
return true;
}
bool ReadFloat (out int value)
{
var convert_buffer = new byte[4];
if (4 != m_file.Read (convert_buffer, 0, 4))
{
value = 0;
return false;
}
if (m_is_bigendian)
Array.Reverse (convert_buffer);
value = (int)BitConverter.ToSingle (convert_buffer, 0);
return true;
}
bool ReadDouble (out int value)
{
var convert_buffer = new byte[8];
if (8 != m_file.Read (convert_buffer, 0, 8))
{
value = 0;
return false;
}
if (m_is_bigendian)
Array.Reverse (convert_buffer);
long bits = BitConverter.ToInt64 (convert_buffer, 0);
value = (int)BitConverter.Int64BitsToDouble (bits);
return true;
}
#region IDisposable Members
bool disposed = false;
public void Dispose ()
{
Dispose (true);
GC.SuppressFinalize (this);
}
protected virtual void Dispose (bool disposing)
{
if (!disposed)
{
if (disposing)
{
m_file.Dispose();
}
m_file = null;
disposed = true;
}
}
#endregion
}
}
}

162
GameRes/MultiDict.cs Normal file
View File

@@ -0,0 +1,162 @@
//! \file GameRes.cs
//! \date Mon Jun 30 20:12:13 2014
//! \brief game resources browser.
//
using System;
using System.Collections.Generic;
namespace GameRes.Collections
{
/// <summary>
/// Extension to the normal Dictionary. This class can store more than one value for every key.
/// It keeps a HashSet for every Key value. Calling Add with the same Key and multiple values
/// will store each value under the same Key in the Dictionary. Obtaining the values for a Key
/// will return the HashSet with the Values of the Key.
/// </summary>
/// <typeparam name="TKey">The type of the key.</typeparam>
/// <typeparam name="TValue">The type of the value.</typeparam>
public class MultiValueDictionary<TKey, TValue> : Dictionary<TKey, HashSet<TValue>> //, IEnumerable<KeyValuePair<TKey, TValue>>, System.Collections.IEnumerable
{
/// <summary>
/// Initializes a new instance of the <see cref="MultiValueDictionary&lt;TKey, TValue&gt;"/> class.
/// </summary>
public MultiValueDictionary() : base()
{
}
/// <summary>
/// Adds the specified value under the specified key
/// </summary>
/// <param name="key">The key.</param>
/// <param name="value">The value.</param>
public void Add(TKey key, TValue value)
{
HashSet<TValue> container = null;
if(!this.TryGetValue(key, out container))
{
container = new HashSet<TValue>();
base.Add(key, container);
}
container.Add(value);
}
/// <summary>
/// Removes the specified value for the specified key. It will leave the key in the dictionary.
/// </summary>
/// <param name="key">The key.</param>
/// <param name="value">The value.</param>
public void Remove(TKey key, TValue value)
{
HashSet<TValue> container = null;
if(this.TryGetValue(key, out container))
{
container.Remove(value);
if(container.Count <= 0)
{
this.Remove(key);
}
}
}
/// <summary>
/// Gets the values for the key specified. This method is useful if you want to avoid an
/// exception for key value retrieval and you can't use TryGetValue (e.g. in lambdas)
/// </summary>
/// <param name="key">The key.</param>
/// <param name="returnEmptySet">if set to true and the key isn't found, an empty hashset is
/// returned, otherwise, if the key isn't found, null is returned</param>
/// <returns>
/// This method will return null (or an empty set if returnEmptySet is true) if the key
/// wasn't found, or the values if key was found.
/// </returns>
public HashSet<TValue> GetValues(TKey key, bool returnEmptySet)
{
HashSet<TValue> toReturn = null;
if (!base.TryGetValue(key, out toReturn) && returnEmptySet)
{
toReturn = new HashSet<TValue>();
}
return toReturn;
}
/*
// hides Dictionary.GetEnumerator()
new public IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator()
{
Enumerator e = new Enumerator();
e.key_enumerator = base.GetEnumerator();
e.current_pair = new KeyValuePair<TKey, TValue>();
if (e.key_enumerator.MoveNext())
e.value_enumerator = e.key_enumerator.Current.Value.GetEnumerator();
else
e.value_enumerator = null;
return e;
}
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
{
return (System.Collections.IEnumerator)GetEnumerator();
}
[SerializableAttribute]
new public struct Enumerator : IEnumerator<KeyValuePair<TKey, TValue>>, IDisposable, System.Collections.IEnumerator
{
public Dictionary<TKey, HashSet<TValue>>.Enumerator key_enumerator;
public HashSet<TValue>.Enumerator? value_enumerator;
public KeyValuePair<TKey, TValue> current_pair;
public KeyValuePair<TKey, TValue> Current { get { return current_pair; } }
object System.Collections.IEnumerator.Current { get { return Current; } }
void IDisposable.Dispose() { }
void System.Collections.IEnumerator.Reset()
{
}
private void SetCurrent ()
{
current_pair = new KeyValuePair<TKey, TValue>(key_enumerator.Current.Key, value_enumerator.Value.Current);
Console.WriteLine("Enumerator.SetCurrent ({0} => {1})", current_pair.Key, current_pair.Value);
}
private void ResetCurrent ()
{
current_pair = new KeyValuePair<TKey, TValue>(default(TKey), default(TValue));
}
public bool MoveNext ()
{
if (null == value_enumerator)
{
ResetCurrent();
return false;
}
if (value_enumerator.Value.MoveNext())
{
SetCurrent();
return true;
}
if (!key_enumerator.MoveNext())
{
value_enumerator = null;
ResetCurrent();
return false;
}
value_enumerator = key_enumerator.Current.Value.GetEnumerator();
if (value_enumerator.Value.MoveNext())
{
SetCurrent();
return true;
}
else
{
current_pair = new KeyValuePair<TKey, TValue>(key_enumerator.Current.Key, default(TValue));
return false;
}
}
}
*/
}
}

View File

@@ -0,0 +1,36 @@
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyTitle("GameRes")]
[assembly: AssemblyDescription("Game Resources class library")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("GameRes")]
[assembly: AssemblyCopyright("Copyright © 2014 mørkt")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
// Setting ComVisible to false makes the types in this assembly not visible
// to COM components. If you need to access a type in this assembly from
// COM, set the ComVisible attribute to true on that type.
[assembly: ComVisible(false)]
// The following GUID is for the ID of the typelib if this project is exposed to COM
[assembly: Guid("224080ad-deb6-4d47-a454-903a14c74cb7")]
// Version information for an assembly consists of the following four values:
//
// Major Version
// Minor Version
// Build Number
// Revision
//
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]

36
GameRes/ScriptText.cs Normal file
View File

@@ -0,0 +1,36 @@
//! \file ScriptText.cs
//! \date Thu Jul 10 11:09:32 2014
//! \brief Script text resource interface.
//
using System.IO;
using System.Text;
using System.Collections.Generic;
namespace GameRes
{
public struct ScriptLine
{
public uint Id;
public string Text;
}
public class ScriptData
{
public ICollection<ScriptLine> TextLines { get { return m_text; } }
protected List<ScriptLine> m_text = new List<ScriptLine>();
/*
public abstract void Serialize (Stream output);
public abstract void Deserialize (Stream input);
*/
}
public abstract class ScriptFormat : IResource
{
public override string Type { get { return "script"; } }
public abstract ScriptData Read (string name, Stream file);
public abstract void Write (Stream file, ScriptData script);
}
}

90
GameRes/Strings/garStrings.Designer.cs generated Normal file
View File

@@ -0,0 +1,90 @@
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by a tool.
// Runtime Version:4.0.30319.18444
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------
namespace GameRes.Strings {
using System;
/// <summary>
/// A strongly-typed resource class, for looking up localized strings, etc.
/// </summary>
// This class was auto-generated by the StronglyTypedResourceBuilder
// class via a tool like ResGen or Visual Studio.
// To add or remove a member, edit your .ResX file then rerun ResGen
// with the /str option, or rebuild your VS project.
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
public class garStrings {
private static global::System.Resources.ResourceManager resourceMan;
private static global::System.Globalization.CultureInfo resourceCulture;
[global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
internal garStrings() {
}
/// <summary>
/// Returns the cached ResourceManager instance used by this class.
/// </summary>
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
public static global::System.Resources.ResourceManager ResourceManager {
get {
if (object.ReferenceEquals(resourceMan, null)) {
global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("GameRes.Strings.garStrings", typeof(garStrings).Assembly);
resourceMan = temp;
}
return resourceMan;
}
}
/// <summary>
/// Overrides the current thread's CurrentUICulture property for all
/// resource lookups using this strongly typed resource class.
/// </summary>
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
public static global::System.Globalization.CultureInfo Culture {
get {
return resourceCulture;
}
set {
resourceCulture = value;
}
}
/// <summary>
/// Looks up a localized string similar to Inappropriate encryption scheme.
/// </summary>
public static string MsgInvalidEncryption {
get {
return ResourceManager.GetString("MsgInvalidEncryption", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Invalid file format.
/// </summary>
public static string MsgInvalidFormat {
get {
return ResourceManager.GetString("MsgInvalidFormat", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Unknown encryption scheme.
/// </summary>
public static string MsgUnknownEncryption {
get {
return ResourceManager.GetString("MsgUnknownEncryption", resourceCulture);
}
}
}
}

View File

@@ -0,0 +1,129 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<data name="MsgInvalidEncryption" xml:space="preserve">
<value>Inappropriate encryption scheme</value>
</data>
<data name="MsgInvalidFormat" xml:space="preserve">
<value>Invalid file format</value>
</data>
<data name="MsgUnknownEncryption" xml:space="preserve">
<value>Unknown encryption scheme</value>
</data>
</root>

View File

@@ -0,0 +1,129 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<data name="MsgInvalidEncryption" xml:space="preserve">
<value>Неподходящий метод шифрования</value>
</data>
<data name="MsgInvalidFormat" xml:space="preserve">
<value>Некорректный формат файла</value>
</data>
<data name="MsgUnknownEncryption" xml:space="preserve">
<value>Неизвестный метод шифрования</value>
</data>
</root>

235
GameRes/Utility.cs Normal file
View File

@@ -0,0 +1,235 @@
//! \file Utility.cs
//! \date Sat Jul 05 02:47:33 2014
//! \brief utility class for GameRes assembly.
//
namespace GameRes.Utility
{
public static class Binary
{
public static uint BigEndian (uint u)
{
return (u & 0xff) << 24 | (u & 0xff00) << 8 | (u & 0xff0000) >> 8 | (u & 0xff000000) >> 24;
}
public static int BigEndian (int i)
{
return (int)BigEndian ((uint)i);
}
public static ushort BigEndian (ushort u)
{
return (ushort)((u & 0xff) << 8 | (u & 0xff00) >> 8);
}
public static short BigEndian (short i)
{
return (short)BigEndian ((ushort)i);
}
public static ulong BigEndian (ulong u)
{
return (ulong)BigEndian((uint)(u & 0xffffffff)) << 32
| (ulong)BigEndian((uint)(u >> 32));
}
public static long BigEndian (long i)
{
return (long)BigEndian ((ulong)i);
}
public static bool AsciiEqual (byte[] name1, string name2)
{
return AsciiEqual (name1, 0, name2);
}
public static bool AsciiEqual (byte[] name1, int offset, string name2)
{
if (name1.Length-offset < name2.Length)
return false;
for (int i = 0; i < name2.Length; ++i)
if ((char)name1[offset+i] != name2[i])
return false;
return true;
}
}
public static class LittleEndian
{
public static ushort ToUInt16 (byte[] value, int index)
{
return (ushort)(value[index] | value[index+1] << 8);
}
public static short ToInt16 (byte[] value, int index)
{
return (short)(value[index] | value[index+1] << 8);
}
public static uint ToUInt32 (byte[] value, int index)
{
return (uint)(value[index] | value[index+1] << 8 | value[index+2] << 16 | value[index+3] << 24);
}
public static int ToInt32 (byte[] value, int index)
{
return (int)ToUInt32 (value, index);
}
}
public sealed class Crc32
{
/* Table of CRCs of all 8-bit messages. */
private static readonly uint[] crc_table = InitializeTable();
/* Make the table for a fast CRC. */
private static uint[] InitializeTable ()
{
uint[] table = new uint[256];
for (uint n = 0; n < 256; n++)
{
uint c = n;
for (int k = 0; k < 8; k++)
{
if (0 != (c & 1))
c = 0xedb88320 ^ (c >> 1);
else
c = c >> 1;
}
table[n] = c;
}
return table;
}
/* Update a running CRC with the bytes buf[0..len-1]--the CRC
should be initialized to all 1's, and the transmitted value
is the 1's complement of the final running CRC (see the
crc() routine below)). */
static uint UpdateCrc (uint crc, byte[] buf, int pos, int len)
{
uint c = crc;
for (int n = 0; n < len; n++)
c = crc_table[(c ^ buf[pos+n]) & 0xff] ^ (c >> 8);
return c;
}
/* Return the CRC of the bytes buf[0..len-1]. */
public static uint Compute (byte[] buf, int pos, int len)
{
return UpdateCrc (0xffffffff, buf, pos, len) ^ 0xffffffff;
}
private uint m_crc = 0xffffffff;
public uint Value { get { return m_crc^0xffffffff; } }
public void Update (byte[] buf, int pos, int len)
{
m_crc = UpdateCrc (m_crc, buf, pos, len);
}
}
public sealed class Adler32
{
const uint BASE = 65521; /* largest prime smaller than 65536 */
const int NMAX = 5552;
public static uint Compute (byte[] buf, int pos, int len)
{
return Update (1, buf, pos, len);
}
private static uint Update (uint adler, byte[] buf, int pos, int len)
{
/* split Adler-32 into component sums */
uint sum2 = (adler >> 16) & 0xffff;
adler &= 0xffff;
/* in case user likes doing a byte at a time, keep it fast */
if (1 == len) {
adler += buf[pos];
if (adler >= BASE)
adler -= BASE;
sum2 += adler;
if (sum2 >= BASE)
sum2 -= BASE;
return adler | (sum2 << 16);
}
/* in case short lengths are provided, keep it somewhat fast */
if (len < 16) {
while (0 != len--) {
adler += buf[pos++];
sum2 += adler;
}
if (adler >= BASE)
adler -= BASE;
sum2 %= BASE; /* only added so many BASE's */
return adler | (sum2 << 16);
}
/* do length NMAX blocks -- requires just one modulo operation */
while (len >= NMAX) {
len -= NMAX;
int n = NMAX / 16; /* NMAX is divisible by 16 */
do {
/* 16 sums unrolled */
adler += buf[pos]; sum2 += adler;
adler += buf[pos+1]; sum2 += adler;
adler += buf[pos+2]; sum2 += adler;
adler += buf[pos+3]; sum2 += adler;
adler += buf[pos+4]; sum2 += adler;
adler += buf[pos+5]; sum2 += adler;
adler += buf[pos+6]; sum2 += adler;
adler += buf[pos+7]; sum2 += adler;
adler += buf[pos+8]; sum2 += adler;
adler += buf[pos+9]; sum2 += adler;
adler += buf[pos+10]; sum2 += adler;
adler += buf[pos+11]; sum2 += adler;
adler += buf[pos+12]; sum2 += adler;
adler += buf[pos+13]; sum2 += adler;
adler += buf[pos+14]; sum2 += adler;
adler += buf[pos+15]; sum2 += adler;
pos += 16;
} while (0 != --n);
adler %= BASE;
sum2 %= BASE;
}
/* do remaining bytes (less than NMAX, still just one modulo) */
if (0 != len) { /* avoid modulos if none remaining */
while (len >= 16) {
len -= 16;
adler += buf[pos]; sum2 += adler;
adler += buf[pos+1]; sum2 += adler;
adler += buf[pos+2]; sum2 += adler;
adler += buf[pos+3]; sum2 += adler;
adler += buf[pos+4]; sum2 += adler;
adler += buf[pos+5]; sum2 += adler;
adler += buf[pos+6]; sum2 += adler;
adler += buf[pos+7]; sum2 += adler;
adler += buf[pos+8]; sum2 += adler;
adler += buf[pos+9]; sum2 += adler;
adler += buf[pos+10]; sum2 += adler;
adler += buf[pos+11]; sum2 += adler;
adler += buf[pos+12]; sum2 += adler;
adler += buf[pos+13]; sum2 += adler;
adler += buf[pos+14]; sum2 += adler;
adler += buf[pos+15]; sum2 += adler;
pos += 16;
}
while (0 != len--) {
adler += buf[pos++];
sum2 += adler;
}
adler %= BASE;
sum2 %= BASE;
}
/* return recombined sums */
return adler | (sum2 << 16);
}
private uint m_adler = 1;
public uint Value { get { return m_adler; } }
public void Update (byte[] buf, int pos, int len)
{
m_adler = Update (m_adler, buf, pos, len);
}
}
}