mirror of
https://github.com/lifegpc/GARbro.git
synced 2026-07-08 01:31:41 +08:00
Initial commit.
This commit is contained in:
245
GameRes/ArcFile.cs
Normal file
245
GameRes/ArcFile.cs
Normal 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
470
GameRes/ArcView.cs
Normal 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
347
GameRes/GameRes.cs
Normal 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
89
GameRes/GameRes.csproj
Normal 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
100
GameRes/Image.cs
Normal 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
81
GameRes/ImageBMP.cs
Normal 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
79
GameRes/ImageJPEG.cs
Normal 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
195
GameRes/ImagePNG.cs
Normal 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
204
GameRes/ImageTGA.cs
Normal 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
363
GameRes/ImageTIFF.cs
Normal 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
162
GameRes/MultiDict.cs
Normal 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<TKey, TValue>"/> 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
*/
|
||||
}
|
||||
}
|
||||
36
GameRes/Properties/AssemblyInfo.cs
Normal file
36
GameRes/Properties/AssemblyInfo.cs
Normal 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
36
GameRes/ScriptText.cs
Normal 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
90
GameRes/Strings/garStrings.Designer.cs
generated
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
129
GameRes/Strings/garStrings.resx
Normal file
129
GameRes/Strings/garStrings.resx
Normal 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>
|
||||
129
GameRes/Strings/garStrings.ru-RU.resx
Normal file
129
GameRes/Strings/garStrings.ru-RU.resx
Normal 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
235
GameRes/Utility.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user