diff --git a/GUI/GarExtract.cs b/GUI/GarExtract.cs
index a216ddb7..c6c406bd 100644
--- a/GUI/GarExtract.cs
+++ b/GUI/GarExtract.cs
@@ -306,31 +306,35 @@ namespace GARbro.GUI
void ExtractImage (ArcFile arc, Entry entry, ImageFormat target_format)
{
- using (var file = arc.OpenBinaryEntry (entry))
+ try
{
- var src_format = ImageFormat.FindFormat (file);
- if (null == src_format)
- throw new InvalidFormatException (string.Format ("{1}: {0}", guiStrings.MsgUnableInterpretImage, entry.Name));
- file.Position = 0;
- string target_ext = target_format.Extensions.FirstOrDefault() ?? "";
- string outname = FindUniqueFileName (entry.Name, target_ext);
- if (src_format.Item1 == target_format)
+ using (var decoder = arc.OpenImage (entry))
{
- // source format is the same as a target, copy file as is
- using (var output = ArchiveFormat.CreateFile (outname))
- file.AsStream.CopyTo (output);
- return;
- }
- ImageData image = src_format.Item1.Read (file, src_format.Item2);
- if (m_adjust_image_offset)
- {
- image = AdjustImageOffset (image);
- }
- using (var outfile = ArchiveFormat.CreateFile (outname))
- {
- target_format.Write (outfile, image);
+ var src_format = decoder.Format; // could be null
+ string target_ext = target_format.Extensions.FirstOrDefault() ?? "";
+ string outname = FindUniqueFileName (entry.Name, target_ext);
+ if (src_format == target_format)
+ {
+ // source format is the same as a target, copy file as is
+ using (var output = ArchiveFormat.CreateFile (outname))
+ decoder.Input.CopyTo (output);
+ return;
+ }
+ ImageData image = decoder.Image;
+ if (m_adjust_image_offset)
+ {
+ image = AdjustImageOffset (image);
+ }
+ using (var outfile = ArchiveFormat.CreateFile (outname))
+ {
+ target_format.Write (outfile, image);
+ }
}
}
+ catch
+ {
+ throw new InvalidFormatException (string.Format ("{1}: {0}", guiStrings.MsgUnableInterpretImage, entry.Name));
+ }
}
static ImageData AdjustImageOffset (ImageData image)
diff --git a/GUI/ImagePreview.cs b/GUI/ImagePreview.cs
index f1caab06..75c8bb1a 100644
--- a/GUI/ImagePreview.cs
+++ b/GUI/ImagePreview.cs
@@ -225,13 +225,9 @@ namespace GARbro.GUI
{
try
{
- using (var file = VFS.OpenBinaryStream (preview.Entry))
+ using (var data = VFS.OpenImage (preview.Entry))
{
- var data = ImageFormat.Read (file);
- if (null != data)
- SetPreviewImage (preview, data.Bitmap);
- else
- Trace.WriteLine ("Cannot parse image format", preview.Name);
+ SetPreviewImage (preview, data.Image.Bitmap);
}
}
catch (Exception X)
diff --git a/GameRes/ArcFile.cs b/GameRes/ArcFile.cs
index d0d060ec..7afc2d31 100644
--- a/GameRes/ArcFile.cs
+++ b/GameRes/ArcFile.cs
@@ -217,6 +217,11 @@ namespace GameRes
return BinaryStream.FromStream (input, entry.Name);
}
+ public IImageDecoder OpenImage (Entry entry)
+ {
+ return m_interface.OpenImage (this, entry);
+ }
+
public ArchiveFileSystem CreateFileSystem ()
{
if (m_interface.IsHierarchic)
diff --git a/GameRes/FileSystem.cs b/GameRes/FileSystem.cs
index 96ad69a5..879f00c2 100644
--- a/GameRes/FileSystem.cs
+++ b/GameRes/FileSystem.cs
@@ -722,6 +722,17 @@ namespace GameRes
return m_vfs.Top.OpenView (entry);
}
+ public static IImageDecoder OpenImage (Entry entry)
+ {
+ var fs = m_vfs.Top;
+ var arc_fs = fs as ArchiveFileSystem;
+ if (arc_fs != null)
+ return arc_fs.Source.OpenImage (entry);
+
+ var input = fs.OpenBinaryStream (entry);
+ return new ImageStreamDecoder (input);
+ }
+
public static Stream OpenStream (string filename)
{
return m_vfs.Top.OpenStream (m_vfs.Top.FindFile (filename));
diff --git a/GameRes/GameRes.cs b/GameRes/GameRes.cs
index c8da1a68..9de0bf48 100644
--- a/GameRes/GameRes.cs
+++ b/GameRes/GameRes.cs
@@ -197,6 +197,15 @@ namespace GameRes
return arc.File.CreateStream (entry.Offset, entry.Size, entry.Name);
}
+ ///
+ /// Open as image. Throws InvalidFormatException if entry is not an image.
+ ///
+ public virtual IImageDecoder OpenImage (ArcFile arc, Entry entry)
+ {
+ var input = arc.OpenBinaryEntry (entry);
+ return new ImageStreamDecoder (input);
+ }
+
///
/// Create file corresponding to in current directory and open it
/// for writing. Overwrites existing file, if any.
diff --git a/GameRes/Image.cs b/GameRes/Image.cs
index 6a9e5d73..06d5d9b3 100644
--- a/GameRes/Image.cs
+++ b/GameRes/Image.cs
@@ -186,4 +186,70 @@ namespace GameRes
public static ImageFormat Bmp { get { return s_BmpFormat.Value; } }
public static ImageFormat Tga { get { return s_TgaFormat.Value; } }
}
+
+ public interface IImageDecoder : IDisposable
+ {
+ Stream Input { get; }
+
+ ///
+ /// Underlying image format or null if image is not represented by any format.
+ ///
+ ImageFormat Format { get; }
+
+ ///
+ /// Image parameters.
+ ///
+ ImageMetaData Info { get; }
+
+ ///
+ /// Decoded image data.
+ ///
+ ImageData Image { get; }
+ }
+
+ public sealed class ImageStreamDecoder : IImageDecoder
+ {
+ IBinaryStream m_file;
+ ImageFormat m_format;
+ ImageMetaData m_info;
+ ImageData m_image;
+
+ public Stream Input { get { m_file.Position = 0; return m_file.AsStream; } }
+
+ public ImageFormat Format { get { return m_format; } }
+ public ImageMetaData Info { get { return m_info; } }
+
+ public ImageData Image
+ {
+ get
+ {
+ if (null == m_image)
+ {
+ m_file.Position = 0;
+ m_image = m_format.Read (m_file, m_info);
+ }
+ return m_image;
+ }
+ }
+
+ public ImageStreamDecoder (IBinaryStream file)
+ {
+ m_file = file;
+ var format = ImageFormat.FindFormat (file);
+ if (null == format)
+ throw new InvalidFormatException();
+ m_format = format.Item1;
+ m_info = format.Item2;
+ }
+
+ bool m_disposed = false;
+ public void Dispose ()
+ {
+ if (!m_disposed)
+ {
+ m_file.Dispose();
+ m_disposed = true;
+ }
+ }
+ }
}