mirror of
https://github.com/crskycode/GARbro.git
synced 2026-06-06 13:48:57 +08:00
Compare commits
37 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
df66b70a38 | ||
|
|
bf2acd076e | ||
|
|
c43368150e | ||
|
|
0bc57de734 | ||
|
|
30e7a470d3 | ||
|
|
9d88975f13 | ||
|
|
b334222997 | ||
|
|
0955c15181 | ||
|
|
ab70e8b9f8 | ||
|
|
4c089120d1 | ||
|
|
227eae0dbe | ||
|
|
d7030ecbdc | ||
|
|
230f8b8e9f | ||
|
|
104e32a150 | ||
|
|
df9193bf11 | ||
|
|
cf721eab0d | ||
|
|
10a52b7f5a | ||
|
|
d7899fa852 | ||
|
|
490bf8eaff | ||
|
|
1200a610f4 | ||
|
|
b945a20642 | ||
|
|
dfcf169f61 | ||
|
|
29bd97fca9 | ||
|
|
fc27ece34d | ||
|
|
1f9e4d7b33 | ||
|
|
e7672ceb2c | ||
|
|
e20853967f | ||
|
|
6195ab25e3 | ||
|
|
6e9f5f7378 | ||
|
|
460f6b4447 | ||
|
|
50eb805cd2 | ||
|
|
7f8b091d05 | ||
|
|
34a2391811 | ||
|
|
eb5392a16b | ||
|
|
2da5ed1961 | ||
|
|
8849508e41 | ||
|
|
41c76ae50e |
@@ -85,6 +85,9 @@
|
||||
<setting name="appExtractAudio" serializeAs="String">
|
||||
<value>True</value>
|
||||
</setting>
|
||||
<setting name="appIgnoreConversionErrors" serializeAs="String">
|
||||
<value>False</value>
|
||||
</setting>
|
||||
</GARbro.GUI.Properties.Settings>
|
||||
</userSettings>
|
||||
</configuration>
|
||||
|
||||
@@ -187,26 +187,137 @@ namespace GameRes.Formats
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Represents a region within existing stream.
|
||||
/// Underlying stream should allow seeking (CanSeek == true).
|
||||
/// </summary>
|
||||
public class StreamRegion : Stream
|
||||
{
|
||||
private Stream m_stream;
|
||||
private long m_begin;
|
||||
private long m_end;
|
||||
private bool m_should_dispose;
|
||||
|
||||
public StreamRegion (Stream main, long offset, long length, bool leave_open = false)
|
||||
{
|
||||
m_stream = main;
|
||||
m_begin = offset;
|
||||
m_end = Math.Min (offset + length, m_stream.Length);
|
||||
m_stream.Position = m_begin;
|
||||
m_should_dispose = !leave_open;
|
||||
}
|
||||
|
||||
public StreamRegion (Stream main, long offset) : this (main, offset, main.Length-offset)
|
||||
{
|
||||
}
|
||||
|
||||
public override bool CanRead { get { return m_stream.CanRead; } }
|
||||
public override bool CanSeek { get { return true; } }
|
||||
public override bool CanWrite { get { return false; } }
|
||||
public override long Length { get { return m_end - m_begin; } }
|
||||
public override long Position
|
||||
{
|
||||
get { return m_stream.Position - m_begin; }
|
||||
set { m_stream.Position = Math.Max (m_begin + value, m_begin); }
|
||||
}
|
||||
|
||||
public override void Flush()
|
||||
{
|
||||
m_stream.Flush();
|
||||
}
|
||||
|
||||
public override long Seek (long offset, SeekOrigin origin)
|
||||
{
|
||||
if (SeekOrigin.Begin == origin)
|
||||
offset += m_begin;
|
||||
else if (SeekOrigin.Current == origin)
|
||||
offset += m_stream.Position;
|
||||
else
|
||||
offset += m_end;
|
||||
offset = Math.Max (offset, m_begin);
|
||||
m_stream.Position = offset;
|
||||
return offset - m_begin;
|
||||
}
|
||||
|
||||
public override int Read (byte[] buffer, int offset, int count)
|
||||
{
|
||||
int read = 0;
|
||||
long available = m_end - m_stream.Position;
|
||||
if (available > 0)
|
||||
{
|
||||
read = m_stream.Read (buffer, offset, (int)Math.Min (count, available));
|
||||
}
|
||||
return read;
|
||||
}
|
||||
|
||||
public override int ReadByte ()
|
||||
{
|
||||
if (m_stream.Position < m_end)
|
||||
return m_stream.ReadByte();
|
||||
else
|
||||
return -1;
|
||||
}
|
||||
|
||||
public override void SetLength (long length)
|
||||
{
|
||||
throw new NotSupportedException ("StreamRegion.SetLength method is not supported");
|
||||
}
|
||||
|
||||
public override void Write (byte[] buffer, int offset, int count)
|
||||
{
|
||||
throw new NotSupportedException ("StreamRegion.Write method is not supported");
|
||||
}
|
||||
|
||||
public override void WriteByte (byte value)
|
||||
{
|
||||
throw new NotSupportedException ("StreamRegion.WriteByte method is not supported");
|
||||
}
|
||||
|
||||
bool m_disposed = false;
|
||||
protected override void Dispose (bool disposing)
|
||||
{
|
||||
if (!m_disposed)
|
||||
{
|
||||
if (m_should_dispose)
|
||||
m_stream.Dispose();
|
||||
m_disposed = true;
|
||||
base.Dispose (disposing);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class LzssReader : IDisposable
|
||||
{
|
||||
BinaryReader m_input;
|
||||
byte[] m_output;
|
||||
int m_size;
|
||||
|
||||
public byte[] Data { get { return m_output; } }
|
||||
public BinaryReader Input { get { return m_input; } }
|
||||
public byte[] Data { get { return m_output; } }
|
||||
public int FrameSize { get; set; }
|
||||
public byte FrameFill { get; set; }
|
||||
public int FrameInitPos { get; set; }
|
||||
|
||||
public LzssReader (Stream input, int input_length, int output_length)
|
||||
{
|
||||
m_input = new BinaryReader (input, Encoding.ASCII, true);
|
||||
m_output = new byte[output_length];
|
||||
m_size = input_length;
|
||||
|
||||
FrameSize = 0x1000;
|
||||
FrameFill = 0;
|
||||
FrameInitPos = 0xfee;
|
||||
}
|
||||
|
||||
public void Unpack ()
|
||||
{
|
||||
int dst = 0;
|
||||
var frame = new byte[0x1000];
|
||||
int frame_pos = 0xfee;
|
||||
var frame = new byte[FrameSize];
|
||||
if (FrameFill != 0)
|
||||
for (int i = 0; i < frame.Length; ++i)
|
||||
frame[i] = FrameFill;
|
||||
int frame_pos = FrameInitPos;
|
||||
int frame_mask = FrameSize-1;
|
||||
int remaining = (int)m_size;
|
||||
while (remaining > 0)
|
||||
{
|
||||
@@ -221,7 +332,7 @@ namespace GameRes.Formats
|
||||
byte b = m_input.ReadByte();
|
||||
--remaining;
|
||||
frame[frame_pos++] = b;
|
||||
frame_pos &= 0xfff;
|
||||
frame_pos &= frame_mask;
|
||||
m_output[dst++] = b;
|
||||
}
|
||||
else
|
||||
@@ -237,9 +348,9 @@ namespace GameRes.Formats
|
||||
if (dst >= m_output.Length)
|
||||
break;
|
||||
byte v = frame[offset++];
|
||||
offset &= 0xfff;
|
||||
offset &= frame_mask;
|
||||
frame[frame_pos++] = v;
|
||||
frame_pos &= 0xfff;
|
||||
frame_pos &= frame_mask;
|
||||
m_output[dst++] = v;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -128,7 +128,11 @@ namespace GameRes.Formats.DRS
|
||||
if (0 == name_length)
|
||||
return null;
|
||||
string name = encoding.GetString (name_raw, 0, name_length).ToLowerInvariant();
|
||||
var entry = FormatCatalog.Instance.CreateEntry (name);
|
||||
Entry entry;
|
||||
if (name.EndsWith (".isf", System.StringComparison.InvariantCultureIgnoreCase))
|
||||
entry = new Entry { Name = name, Type = "script" };
|
||||
else
|
||||
entry = FormatCatalog.Instance.CreateEntry (name);
|
||||
entry.Offset = file.View.ReadUInt32 (dir_offset+12);
|
||||
entry.Size = file.View.ReadUInt32 (dir_offset+16);
|
||||
if (!entry.CheckPlacement (file.MaxOffset))
|
||||
|
||||
@@ -108,8 +108,11 @@
|
||||
<Compile Include="ArcNPA.cs" />
|
||||
<Compile Include="ArcNSA.cs" />
|
||||
<Compile Include="ArcPAC.cs" />
|
||||
<Compile Include="ArcPCK.cs" />
|
||||
<Compile Include="ArcPD.cs" />
|
||||
<Compile Include="ArcQLIE.cs" />
|
||||
<Compile Include="ArcRPA.cs" />
|
||||
<Compile Include="ArcS25.cs" />
|
||||
<Compile Include="ArcSAF.cs" />
|
||||
<Compile Include="ArcSPack.cs" />
|
||||
<Compile Include="ArcSteinsGate.cs" />
|
||||
@@ -118,6 +121,7 @@
|
||||
<Compile Include="ArcXFL.cs" />
|
||||
<Compile Include="ArcXP3.cs" />
|
||||
<Compile Include="ArcYPF.cs" />
|
||||
<Compile Include="AudioEOG.cs" />
|
||||
<Compile Include="AudioMIO.cs" />
|
||||
<Compile Include="AudioMP3.cs" />
|
||||
<Compile Include="AudioOGV.cs" />
|
||||
@@ -163,6 +167,8 @@
|
||||
<Compile Include="ImageBMD.cs" />
|
||||
<Compile Include="ImageBMZ.cs" />
|
||||
<Compile Include="ImageCPB.cs" />
|
||||
<Compile Include="ImageCWL.cs" />
|
||||
<Compile Include="ImageCWP.cs" />
|
||||
<Compile Include="ImageDGC.cs" />
|
||||
<Compile Include="ImageDRG.cs" />
|
||||
<Compile Include="ImageEDT.cs" />
|
||||
@@ -170,6 +176,7 @@
|
||||
<Compile Include="ImageEPA.cs" />
|
||||
<Compile Include="ImageERI.cs" />
|
||||
<Compile Include="ImageGCP.cs" />
|
||||
<Compile Include="ImageGGP.cs" />
|
||||
<Compile Include="ImageGR.cs" />
|
||||
<Compile Include="ImageHG3.cs" />
|
||||
<Compile Include="ImageIAF.cs" />
|
||||
@@ -188,6 +195,7 @@
|
||||
<Compile Include="ImageTLG.cs" />
|
||||
<Compile Include="ImageWCG.cs" />
|
||||
<Compile Include="ImageWIP.cs" />
|
||||
<Compile Include="ImageZBM.cs" />
|
||||
<Compile Include="KiriKiriCx.cs" />
|
||||
<Compile Include="KogadoCocotte.cs" />
|
||||
<Compile Include="AudioOGG.cs" />
|
||||
@@ -216,6 +224,9 @@
|
||||
<SubType>Code</SubType>
|
||||
<DependentUpon>WidgetLPK.xaml</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="WidgetMBL.xaml.cs">
|
||||
<DependentUpon>WidgetMBL.xaml</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="WidgetNOA.xaml.cs">
|
||||
<DependentUpon>WidgetNOA.xaml</DependentUpon>
|
||||
</Compile>
|
||||
@@ -320,6 +331,10 @@
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
<SubType>Designer</SubType>
|
||||
</Page>
|
||||
<Page Include="WidgetMBL.xaml">
|
||||
<SubType>Designer</SubType>
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
</Page>
|
||||
<Page Include="WidgetNOA.xaml">
|
||||
<SubType>Designer</SubType>
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
|
||||
@@ -28,10 +28,28 @@ using System.Collections.Generic;
|
||||
using System.ComponentModel.Composition;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using GameRes.Formats.Properties;
|
||||
using GameRes.Formats.Strings;
|
||||
using GameRes.Utility;
|
||||
|
||||
namespace GameRes.Formats.Marble
|
||||
{
|
||||
public class MblOptions : ResourceOptions
|
||||
{
|
||||
public string PassPhrase;
|
||||
}
|
||||
|
||||
public class MblArchive : ArcFile
|
||||
{
|
||||
public readonly byte[] Key;
|
||||
|
||||
public MblArchive (ArcView arc, ArchiveFormat impl, ICollection<Entry> dir, string password)
|
||||
: base (arc, impl, dir)
|
||||
{
|
||||
Key = Encodings.cp932.GetBytes (password);
|
||||
}
|
||||
}
|
||||
|
||||
[Export(typeof(ArchiveFormat))]
|
||||
public class MblOpener : ArchiveFormat
|
||||
{
|
||||
@@ -64,6 +82,7 @@ namespace GameRes.Formats.Marble
|
||||
return null;
|
||||
try
|
||||
{
|
||||
bool contains_scripts = false;
|
||||
var dir = new List<Entry> (count);
|
||||
for (int i = 0; i < count; ++i)
|
||||
{
|
||||
@@ -79,13 +98,25 @@ namespace GameRes.Formats.Marble
|
||||
name = name.ToLowerInvariant();
|
||||
index_offset += (uint)filename_len;
|
||||
uint offset = file.View.ReadUInt32 (index_offset);
|
||||
var entry = new AutoEntry (name, () => {
|
||||
uint signature = file.View.ReadUInt32 (offset);
|
||||
var res = FormatCatalog.Instance.LookupSignature (signature);
|
||||
if (!res.Any() && 0x4259 == (0xffff & signature))
|
||||
res = FormatCatalog.Instance.ImageFormats.Where (x => x.Tag == "PRS");
|
||||
return res.FirstOrDefault();
|
||||
});
|
||||
Entry entry;
|
||||
if (name.EndsWith (".s"))
|
||||
{
|
||||
entry = new Entry { Name = name, Type = "script" };
|
||||
contains_scripts = true;
|
||||
}
|
||||
else if (name.EndsWith (".prs"))
|
||||
{
|
||||
entry = new Entry { Name = name, Type = "image" };
|
||||
}
|
||||
else
|
||||
{
|
||||
entry = new AutoEntry (name, () => {
|
||||
uint signature = file.View.ReadUInt32 (offset);
|
||||
if (0x4259 == (0xffff & signature))
|
||||
return FormatCatalog.Instance.ImageFormats.FirstOrDefault (x => x.Tag == "PRS");
|
||||
return FormatCatalog.Instance.LookupSignature (signature).FirstOrDefault();
|
||||
});
|
||||
}
|
||||
entry.Offset = offset;
|
||||
entry.Size = file.View.ReadUInt32 (index_offset+4);
|
||||
if (offset < index_size || !entry.CheckPlacement (file.MaxOffset))
|
||||
@@ -95,6 +126,12 @@ namespace GameRes.Formats.Marble
|
||||
}
|
||||
if (0 == dir.Count)
|
||||
return null;
|
||||
if (contains_scripts)
|
||||
{
|
||||
var options = Query<MblOptions> (arcStrings.MBLNotice);
|
||||
if (options.PassPhrase.Length > 0)
|
||||
return new MblArchive (file, this, dir, options.PassPhrase);
|
||||
}
|
||||
return new ArcFile (file, this, dir);
|
||||
}
|
||||
catch
|
||||
@@ -102,5 +139,44 @@ namespace GameRes.Formats.Marble
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public static Dictionary<string, string> KnownKeys = new Dictionary<string, string> {
|
||||
{ arcStrings.ArcDefault, "" },
|
||||
{ "Chikatetsu Fuusa Jiken", "naze" }
|
||||
};
|
||||
|
||||
public override Stream OpenEntry (ArcFile arc, Entry entry)
|
||||
{
|
||||
if (entry.Type != "script" || !entry.Name.EndsWith (".s"))
|
||||
return arc.File.CreateStream (entry.Offset, entry.Size);
|
||||
var marc = arc as MblArchive;
|
||||
var data = new byte[entry.Size];
|
||||
arc.File.View.Read (entry.Offset, data, 0, entry.Size);
|
||||
if (null == marc || null == marc.Key)
|
||||
{
|
||||
for (int i = 0; i < data.Length; ++i)
|
||||
{
|
||||
data[i] = (byte)-data[i];
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int i = 0; i < data.Length; ++i)
|
||||
{
|
||||
data[i] ^= marc.Key[i % marc.Key.Length];
|
||||
}
|
||||
}
|
||||
return new MemoryStream (data);
|
||||
}
|
||||
|
||||
public override ResourceOptions GetDefaultOptions ()
|
||||
{
|
||||
return new MblOptions { PassPhrase = Settings.Default.MBLPassPhrase };
|
||||
}
|
||||
|
||||
public override object GetAccessWidget ()
|
||||
{
|
||||
return new GUI.WidgetMBL();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -88,6 +88,8 @@ namespace GameRes.Formats.Entis
|
||||
{ "Innyuu Famiresu", new Dictionary<string, string> {
|
||||
{ "d01.dat", "vdiu$43AfUCfh9aksf" },
|
||||
{ "d03.dat", "gaivnwq7365e021gf" } } },
|
||||
{ "Konneko", new Dictionary<string, string> {
|
||||
{ "script.noa", "convini_cat" } } },
|
||||
};
|
||||
|
||||
public override ArcFile TryOpen (ArcView file)
|
||||
@@ -98,7 +100,7 @@ namespace GameRes.Formats.Entis
|
||||
if (0x02000400 != id)
|
||||
return null;
|
||||
var reader = new IndexReader (file);
|
||||
if (!reader.ParseDirEntry (0x40, ""))
|
||||
if (!reader.ParseDirEntry (0x40, "") || 0 == reader.Dir.Count)
|
||||
return null;
|
||||
if (!reader.HasEncrypted)
|
||||
return new ArcFile (file, this, reader.Dir);
|
||||
@@ -243,7 +245,9 @@ namespace GameRes.Formats.Entis
|
||||
|
||||
entry.Offset = base_offset + m_file.View.ReadInt64 (dir_offset);
|
||||
if (!entry.CheckPlacement (m_file.MaxOffset))
|
||||
return false;
|
||||
{
|
||||
entry.Size = (uint)(m_file.MaxOffset - entry.Offset);
|
||||
}
|
||||
dir_offset += 0x10;
|
||||
|
||||
uint extra_length = m_file.View.ReadUInt32 (dir_offset);
|
||||
@@ -295,7 +299,7 @@ namespace GameRes.Formats.Entis
|
||||
protected int m_ptrNextBuf;
|
||||
|
||||
protected Stream m_pFile;
|
||||
protected bool m_nFlagEOF;
|
||||
protected ERISADecodeContext m_pContext;
|
||||
|
||||
public ERISADecodeContext (uint nBufferingSize)
|
||||
{
|
||||
@@ -304,23 +308,30 @@ namespace GameRes.Formats.Entis
|
||||
m_nBufCount = 0;
|
||||
m_ptrBuffer = new byte[nBufferingSize];
|
||||
m_pFile = null;
|
||||
m_nFlagEOF = false;
|
||||
m_pContext = null;
|
||||
}
|
||||
|
||||
public bool EofFlag { get { return m_nFlagEOF; } } // GetEOFFlag
|
||||
|
||||
public void AttachInputFile (Stream file)
|
||||
{
|
||||
m_pFile = file;
|
||||
m_pContext = null;
|
||||
}
|
||||
|
||||
public void AttachInputContext (ERISADecodeContext context)
|
||||
{
|
||||
m_pFile = null;
|
||||
m_pContext = context;
|
||||
}
|
||||
|
||||
public uint ReadNextData (byte[] ptrBuffer, uint nBytes)
|
||||
{
|
||||
if (m_pFile != null)
|
||||
{
|
||||
uint read = (uint)m_pFile.Read (ptrBuffer, 0, (int)nBytes);
|
||||
m_nFlagEOF = read != nBytes;
|
||||
return read;
|
||||
return (uint)m_pFile.Read (ptrBuffer, 0, (int)nBytes);
|
||||
}
|
||||
else if (m_pContext != null)
|
||||
{
|
||||
return m_pContext.DecodeBytes (ptrBuffer, nBytes);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
87
ArcFormats/ArcPCK.cs
Normal file
87
ArcFormats/ArcPCK.cs
Normal file
@@ -0,0 +1,87 @@
|
||||
//! \file ArcPCK.cs
|
||||
//! \date Thu Jun 11 12:58:00 2015
|
||||
//! \brief Crowd engine resource archive.
|
||||
//
|
||||
// Copyright (C) 2015 by morkt
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
// deal in the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
// IN THE SOFTWARE.
|
||||
//
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.Composition;
|
||||
using System.IO;
|
||||
using GameRes.Utility;
|
||||
|
||||
namespace GameRes.Formats.Crowd
|
||||
{
|
||||
[Export(typeof(ArchiveFormat))]
|
||||
public class PckOpener : ArchiveFormat
|
||||
{
|
||||
public override string Tag { get { return "PCK"; } }
|
||||
public override string Description { get { return "Crowd engine resource archive"; } }
|
||||
public override uint Signature { get { return 0; } }
|
||||
public override bool IsHierarchic { get { return false; } }
|
||||
public override bool CanCreate { get { return false; } }
|
||||
|
||||
public override ArcFile TryOpen (ArcView file)
|
||||
{
|
||||
int count = file.View.ReadInt32 (0);
|
||||
if (count <= 0 || count > 0xfffff)
|
||||
return null;
|
||||
long index_offset = 4;
|
||||
uint index_size = (uint)(0xc * count);
|
||||
if (index_size > file.View.Reserve (index_offset, index_size))
|
||||
return null;
|
||||
var dir = new List<Entry>();
|
||||
for (int i = 0; i < count; ++i)
|
||||
{
|
||||
var entry = new Entry {
|
||||
Offset = file.View.ReadUInt32 (index_offset+4),
|
||||
Size = file.View.ReadUInt32 (index_offset+8)
|
||||
};
|
||||
if (entry.Offset < index_size || !entry.CheckPlacement (file.MaxOffset))
|
||||
return null;
|
||||
dir.Add (entry);
|
||||
index_offset += 12;
|
||||
}
|
||||
byte[] name_buf = new byte[260];
|
||||
foreach (var entry in dir)
|
||||
{
|
||||
uint max_len = Math.Min (260u, file.View.Reserve (index_offset, 260));
|
||||
uint n;
|
||||
for (n = 0; n < max_len; ++n)
|
||||
{
|
||||
byte b = file.View.ReadByte (index_offset+n);
|
||||
if (0 == b)
|
||||
break;
|
||||
name_buf[n] = b;
|
||||
}
|
||||
if (0 == n || max_len == n)
|
||||
return null;
|
||||
entry.Name = Encodings.cp932.GetString (name_buf, 0, (int)n);
|
||||
entry.Type = FormatCatalog.Instance.GetTypeFromName (entry.Name);
|
||||
index_offset += n+1;
|
||||
}
|
||||
return new ArcFile (file, this, dir);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
251
ArcFormats/ArcQLIE.cs
Normal file
251
ArcFormats/ArcQLIE.cs
Normal file
@@ -0,0 +1,251 @@
|
||||
//! \file ArcQLIE.cs
|
||||
//! \date Mon Jun 15 04:03:18 2015
|
||||
//! \brief QLIE engine archives implementation.
|
||||
//
|
||||
// Copyright (C) 2015 by morkt
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
// deal in the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
// IN THE SOFTWARE.
|
||||
//
|
||||
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.Composition;
|
||||
using System;
|
||||
using GameRes.Utility;
|
||||
|
||||
namespace GameRes.Formats.Qlie
|
||||
{
|
||||
internal class QlieEntry : PackedEntry
|
||||
{
|
||||
public bool IsEncrypted;
|
||||
public uint Hash;
|
||||
public uint Key;
|
||||
}
|
||||
|
||||
[Export(typeof(ArchiveFormat))]
|
||||
public class PackOpener : ArchiveFormat
|
||||
{
|
||||
public override string Tag { get { return "QLIE/PACK"; } }
|
||||
public override string Description { get { return "QLIE engine resource archive"; } }
|
||||
public override uint Signature { get { return 0; } }
|
||||
public override bool IsHierarchic { get { return true; } }
|
||||
public override bool CanCreate { get { return false; } }
|
||||
|
||||
public PackOpener ()
|
||||
{
|
||||
Extensions = new string [] { "pack" };
|
||||
}
|
||||
|
||||
public override ArcFile TryOpen (ArcView file)
|
||||
{
|
||||
if (file.MaxOffset <= 0x1c)
|
||||
return null;
|
||||
long index_offset = file.MaxOffset - 0x1c;
|
||||
if (!file.View.AsciiEqual (index_offset, "FilePackVer")
|
||||
|| !file.View.AsciiEqual (index_offset+0xC, ".0"))
|
||||
return null;
|
||||
int pack_version = file.View.ReadByte (index_offset+0xB) - '0';
|
||||
if (pack_version != 2)
|
||||
throw new NotSupportedException ("Not supported QLIE archive version");
|
||||
int count = file.View.ReadInt32 (index_offset+0x10);
|
||||
if (count < 0 || count > 0xfffff)
|
||||
return null;
|
||||
index_offset = file.View.ReadInt64 (index_offset+0x14);
|
||||
if (index_offset < 0 || index_offset >= file.MaxOffset)
|
||||
return null;
|
||||
|
||||
int pack_key = 0xC4;
|
||||
var name_buffer = new byte[0x100];
|
||||
var dir = new List<Entry> (count);
|
||||
for (int i = 0; i < count; ++i)
|
||||
{
|
||||
int name_length = file.View.ReadUInt16 (index_offset);
|
||||
if (name_length > name_buffer.Length)
|
||||
name_buffer = new byte[name_length];
|
||||
if (name_length != file.View.Read (index_offset+2, name_buffer, 0, (uint)name_length))
|
||||
return null;
|
||||
|
||||
int key = name_length + (pack_key ^ 0x3e);
|
||||
for (int k = 0; k < name_length; ++k)
|
||||
name_buffer[k] ^= (byte)(((k + 1) ^ key) + k + 1);
|
||||
|
||||
string name = Encodings.cp932.GetString (name_buffer, 0, name_length);
|
||||
var entry = new QlieEntry { Name = name };
|
||||
entry.Type = FormatCatalog.Instance.GetTypeFromName (name);
|
||||
|
||||
index_offset += 2 + name_length;
|
||||
entry.Offset = file.View.ReadInt64 (index_offset);
|
||||
entry.Size = file.View.ReadUInt32 (index_offset+8);
|
||||
if (!entry.CheckPlacement (file.MaxOffset))
|
||||
return null;
|
||||
entry.UnpackedSize = file.View.ReadUInt32 (index_offset+12);
|
||||
entry.IsPacked = 0 != file.View.ReadInt32 (index_offset+0x10);
|
||||
entry.IsEncrypted = 0 != file.View.ReadInt32 (index_offset+0x14);
|
||||
entry.Hash = file.View.ReadUInt32 (index_offset+0x18);
|
||||
if (3 == pack_version)
|
||||
entry.Key = (uint)pack_key;
|
||||
else
|
||||
entry.Key = 0;
|
||||
dir.Add (entry);
|
||||
index_offset += 0x1c;
|
||||
}
|
||||
return new ArcFile (file, this, dir);
|
||||
}
|
||||
|
||||
public override Stream OpenEntry (ArcFile arc, Entry entry)
|
||||
{
|
||||
var qent = entry as QlieEntry;
|
||||
if (null == qent || (!qent.IsEncrypted && !qent.IsPacked))
|
||||
return arc.File.CreateStream (entry.Offset, entry.Size);
|
||||
var data = new byte[entry.Size];
|
||||
if (entry.Size != arc.File.View.Read (entry.Offset, data, 0, entry.Size))
|
||||
return arc.File.CreateStream (entry.Offset, entry.Size);
|
||||
|
||||
if (qent.IsEncrypted)
|
||||
Decrypt (data, 0, data.Length, qent.Key);
|
||||
if (qent.IsPacked)
|
||||
{
|
||||
data = Decompress (data);
|
||||
if (null == data) // probably wrong decryption
|
||||
return arc.File.CreateStream (entry.Offset, entry.Size);
|
||||
}
|
||||
return new MemoryStream (data);
|
||||
}
|
||||
|
||||
private void Decrypt (byte[] buffer, int offset, int length, uint key)
|
||||
{
|
||||
if (offset < 0)
|
||||
throw new ArgumentOutOfRangeException ("offset");
|
||||
if (length > buffer.Length || offset > buffer.Length - length)
|
||||
throw new ArgumentOutOfRangeException ("length");
|
||||
|
||||
var table = new uint[2,2];
|
||||
var xor = new uint[2];
|
||||
|
||||
for (int i = 0; i < 2; ++i)
|
||||
{
|
||||
table[0,i] = 0xA73C5F9Du;
|
||||
table[1,i] = 0xCE24F523u;
|
||||
xor[i] = ((uint)length + key) ^ 0xFEC9753Eu;
|
||||
}
|
||||
unsafe
|
||||
{
|
||||
fixed (byte* raw = buffer)
|
||||
{
|
||||
uint* encoded = (uint*)(raw + offset);
|
||||
for (int i = 0; i < length / 8; ++i)
|
||||
{
|
||||
for (int k = 0; k < 2; ++k)
|
||||
{
|
||||
table[0,k] = (table[0,k] + table[1,k]) ^ xor[k];
|
||||
*encoded ^= table[0,k];
|
||||
xor[k] = *encoded++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private byte[] Decompress (byte[] input)
|
||||
{
|
||||
if (LittleEndian.ToUInt32 (input, 0) != 0xFF435031)
|
||||
return null;
|
||||
|
||||
bool is_16bit = 0 != (input[4] & 1);
|
||||
|
||||
var node = new byte[2,256];
|
||||
var child_node = new byte[256];
|
||||
|
||||
int output_length = LittleEndian.ToInt32 (input, 8);
|
||||
var output = new byte[output_length];
|
||||
|
||||
int src = 12;
|
||||
int dst = 0;
|
||||
while (src < input.Length)
|
||||
{
|
||||
int i, k, count, index;
|
||||
|
||||
for (i = 0; i < 256; i++)
|
||||
node[0,i] = (byte)i;
|
||||
|
||||
for (i = 0; i < 256; )
|
||||
{
|
||||
count = input[src++];
|
||||
|
||||
if (count > 127)
|
||||
{
|
||||
int step = count - 127;
|
||||
i += step;
|
||||
count = 0;
|
||||
}
|
||||
|
||||
if (i > 255)
|
||||
break;
|
||||
|
||||
count++;
|
||||
for (k = 0; k < count; k++)
|
||||
{
|
||||
node[0,i] = input[src++];
|
||||
if (node[0,i] != i)
|
||||
node[1,i] = input[src++];
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
if (is_16bit)
|
||||
{
|
||||
count = LittleEndian.ToUInt16 (input, src);
|
||||
src += 2;
|
||||
}
|
||||
else
|
||||
{
|
||||
count = LittleEndian.ToInt32 (input, src);
|
||||
src += 4;
|
||||
}
|
||||
|
||||
k = 0;
|
||||
for (;;)
|
||||
{
|
||||
if (k > 0)
|
||||
index = child_node[--k];
|
||||
else
|
||||
{
|
||||
if (0 == count)
|
||||
break;
|
||||
count--;
|
||||
index = input[src++];
|
||||
}
|
||||
|
||||
if (node[0,index] == index)
|
||||
output[dst++] = (byte)index;
|
||||
else
|
||||
{
|
||||
child_node[k++] = node[1,index];
|
||||
child_node[k++] = node[0,index];
|
||||
}
|
||||
}
|
||||
}
|
||||
if (dst != output.Length)
|
||||
return null;
|
||||
|
||||
return output;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -43,6 +43,11 @@ namespace GameRes.Formats.ShiinaRio
|
||||
public override bool IsHierarchic { get { return false; } }
|
||||
public override bool CanCreate { get { return false; } }
|
||||
|
||||
public S25Opener ()
|
||||
{
|
||||
Extensions = new string[0];
|
||||
}
|
||||
|
||||
public override ArcFile TryOpen (ArcView file)
|
||||
{
|
||||
int count = file.View.ReadInt32 (4);
|
||||
@@ -59,7 +64,7 @@ namespace GameRes.Formats.ShiinaRio
|
||||
{
|
||||
var entry = new Entry
|
||||
{
|
||||
Name = string.Format ("{0}@{1:D4}.s25img", base_name, i),
|
||||
Name = string.Format ("{0}@{1:D4}.tga", base_name, i),
|
||||
Type = "image",
|
||||
Offset = offset,
|
||||
};
|
||||
@@ -79,5 +84,34 @@ namespace GameRes.Formats.ShiinaRio
|
||||
}
|
||||
return new ArcFile (file, this, dir);
|
||||
}
|
||||
|
||||
public override Stream OpenEntry (ArcFile arc, Entry entry)
|
||||
{
|
||||
// emulate TGA image
|
||||
var offset = entry.Offset;
|
||||
var info = new S25MetaData
|
||||
{
|
||||
Width = arc.File.View.ReadUInt32 (offset),
|
||||
Height = arc.File.View.ReadUInt32 (offset+4),
|
||||
OffsetX = arc.File.View.ReadInt32 (offset+8),
|
||||
OffsetY = arc.File.View.ReadInt32 (offset+12),
|
||||
BPP = 32,
|
||||
FirstOffset = (uint)(offset + 0x14),
|
||||
};
|
||||
using (var input = arc.File.CreateStream (0, (uint)arc.File.MaxOffset))
|
||||
using (var reader = new S25Format.Reader (input, info))
|
||||
{
|
||||
var pixels = reader.Unpack();
|
||||
var header = new byte[0x12];
|
||||
header[2] = 2;
|
||||
LittleEndian.Pack ((short)info.OffsetX, header, 8);
|
||||
LittleEndian.Pack ((short)info.OffsetY, header, 0xa);
|
||||
LittleEndian.Pack ((ushort)info.Width, header, 0xc);
|
||||
LittleEndian.Pack ((ushort)info.Height, header, 0xe);
|
||||
header[0x10] = 32;
|
||||
header[0x11] = 0x20;
|
||||
return new PrefixStream (header, new MemoryStream (pixels));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -603,25 +603,31 @@ namespace GameRes.Formats.ShiinaRio
|
||||
|
||||
internal class EncryptionScheme
|
||||
{
|
||||
public string Name { get; set; }
|
||||
public int Version;
|
||||
public string Name { get; set; }
|
||||
public string OriginalTitle { get; set; }
|
||||
public int Version { get; set; }
|
||||
public int EntryNameSize;
|
||||
public byte[] CryptKey;
|
||||
public uint[] HelperKey;
|
||||
public uint[] HelperKey { get; set; }
|
||||
public byte[] ShiinaImage;
|
||||
public byte[] Region;
|
||||
public byte[] DecodeBin;
|
||||
|
||||
private static CachedResource Resource = new CachedResource();
|
||||
|
||||
public static readonly string DefaultCrypt = "Crypt Type 20011002 - Copyright(C) 2000 Y.Yamada/STUDIO よしくん";
|
||||
public static readonly uint[] ZeroKey = new uint[] { 0, 0, 0, 0, 0 };
|
||||
|
||||
public static EncryptionScheme Create (string name, int version, int entry_name_size,
|
||||
string key1, uint[] key2,
|
||||
string image, string region_src, string decode_bin = null)
|
||||
string image, string region_src, string decode_bin = null,
|
||||
string original_title = "")
|
||||
{
|
||||
var scheme = new EncryptionScheme
|
||||
{
|
||||
Name = name,
|
||||
Version = version,
|
||||
OriginalTitle = original_title,
|
||||
EntryNameSize = entry_name_size,
|
||||
CryptKey = Encodings.cp932.GetBytes (key1),
|
||||
HelperKey = key2,
|
||||
@@ -632,6 +638,22 @@ namespace GameRes.Formats.ShiinaRio
|
||||
scheme.DecodeBin = Resource.Load (decode_bin);
|
||||
return scheme;
|
||||
}
|
||||
|
||||
public static EncryptionScheme Create (int version, string name, string original_title, uint[] key2)
|
||||
{
|
||||
string decode = version < 2490 ? "DecodeV1.bin" : "DecodeV249.bin";
|
||||
string image = version < 2400 ? "ShiinaRio1.png"
|
||||
: version < 2490 ? "ShiinaRio3.jpg"
|
||||
: "ShiinaRio4.jpg";
|
||||
int entry_name_size = version < 2390 ? 0x10 : 0x20;
|
||||
return Create (name, version, entry_name_size, DefaultCrypt, key2,
|
||||
image, "ShiinaRio2.png", decode, original_title);
|
||||
}
|
||||
|
||||
public static EncryptionScheme Create (int version, string name)
|
||||
{
|
||||
return Create (version, name, "", ZeroKey);
|
||||
}
|
||||
}
|
||||
|
||||
internal class Decoder
|
||||
@@ -1186,16 +1208,46 @@ namespace GameRes.Formats.ShiinaRio
|
||||
|
||||
public static EncryptionScheme[] KnownSchemes = new EncryptionScheme[]
|
||||
{
|
||||
EncryptionScheme.Create ("ShiinaRio v2.37", 2370, 0x10,
|
||||
"Crypt Type 20011002 - Copyright(C) 2000 Y.Yamada/STUDIO よしくん",
|
||||
new uint[] { 0xF182C682, 0xE882AA82, 0x718E5896, 0x8183CC82, 0xDAC98283 },
|
||||
"ShiinaRio1.png", "ShiinaRio2.png", "DecodeV1.bin"),
|
||||
EncryptionScheme.Create ("ShiinaRio v2.40", 2400, 0x20,
|
||||
"Crypt Type 20011002 - Copyright(C) 2000 Y.Yamada/STUDIO よしくん",
|
||||
new uint[] { 0x747C887C, 0xA47EA17C, 0xAF7CA77C, 0xA17C747C, 0x0000A47E },
|
||||
"ShiinaRio3.jpg", "ShiinaRio2.png", "DecodeV1.bin"),
|
||||
EncryptionScheme.Create ("ShiinaRio v2.49", 2490, 0x20,
|
||||
"Crypt Type 20011002 - Copyright(C) 2000 Y.Yamada/STUDIO よしくん",
|
||||
EncryptionScheme.Create (2360, "ShiinaRio v2.3x"),
|
||||
EncryptionScheme.Create (2480, "Bloody Rondo", "BLOODY†RONDO",
|
||||
new uint[] { 0xFBFBF8F6, 0xFBE6EDF0, 0xFBF0FA, 0, 0 }),
|
||||
EncryptionScheme.Create (2450, "Chuuchuu Nurse", "ちゅうちゅうナース",
|
||||
new uint[] { 0xB0D1ECD1, 0xECD1F7D1, 0xF7D1B0D1, 0x08D23AD0, 0x13D20BD0 }),
|
||||
EncryptionScheme.Create (2370, "Classmate no Okaa-san", "クラスメイトのお母さん",
|
||||
new uint[] { 0xF182C682, 0xE882AA82, 0x718E5896, 0x8183CC82, 0xDAC98283 }),
|
||||
EncryptionScheme.Create (2470, "Damatte Watashi no Muko ni Nare!", "黙って私のムコになれ!",
|
||||
new uint[] { 0x44075C13, 0x010B4107, 0x05064907, 0x4C07D706, 0x6F074D07 }),
|
||||
EncryptionScheme.Create (2470, "Hana to Otome ni Shukufuku wo Royal Bouquet", "花と乙女に祝福を ロイヤルブーケ",
|
||||
new uint[] { 0xE3A7F1AC, 0xB2AA96AC, 0x4FAAECA7, 0xD5A7BAB0, 0x44A754A7 }),
|
||||
EncryptionScheme.Create (2400, "Helter Skelter", "ヘルタースケルター",
|
||||
new uint[] { 0x747C887C, 0xA47EA17C, 0xAF7CA77C, 0xA17C747C, 0x0000A47E }),
|
||||
EncryptionScheme.Create (2470, "Mahou Shoujo no Taisetsu na Koto", "魔法少女の大切なこと。",
|
||||
new uint[] { 0x51879387, 0x869EBC9E, 0xF480DD93, 0xD993C981, 0xD793A093 }),
|
||||
EncryptionScheme.Create (2460, "Mikoko", "みここ",
|
||||
new uint[] { 0x7DAC51AC, 0x51AC7DAC, 0x7DAC7DAC, 0x7DAC51AC, 0x51AC7DAC }),
|
||||
EncryptionScheme.Create (2390, "Nagagutsu wo Haita Deco", "長靴をはいたデコ",
|
||||
new uint[] { 0x486D887E, 0x0F7DBC73, 0x5D7D327D, 0x997C427D, 0x877EAD7C }),
|
||||
EncryptionScheme.Create (2470, "Nakadashi Trilogy", "なかだしトリロジー",
|
||||
new uint[] { 0x928ADB9E, 0xB087BB87, 0x928ADB9E, 0xB087BB87, 0xB087BB87 }),
|
||||
EncryptionScheme.Create (2470, "Onedari Onapet", "おねだりオナペット",
|
||||
new uint[] { 0xD59CB69C, 0xF69CA09C, 0x779D579D, 0x7C9D679D, 0x0000799D }),
|
||||
EncryptionScheme.Create (2480, "Onegan!", "おねガン!",
|
||||
new uint[] { 0xC881AB81, 0x90804880, 0xAB814A82, 0x4880C881, 0x4A829080 }),
|
||||
EncryptionScheme.Create (2470, "Oreimo Plus", "俺妹プラス",
|
||||
new uint[] { 0x24371528, 0x2822D722, 0x1528F922, 0xD7222437, 0xF9222822 }),
|
||||
EncryptionScheme.Create (2470, "Pure Love!", "ぴゅあらっ!",
|
||||
new uint[] { 0x31500050, 0x35507250, 0x87821350, 0x9D9E9780, 0x00009784 }),
|
||||
EncryptionScheme.Create (2470, "Ren'ai Saimin", "恋愛催眠",
|
||||
new uint[] { 0x6E423C5D, 0x7A5C0947, 0x6E423C5D, 0x7A5C0947, 0x6E423C5D }),
|
||||
EncryptionScheme.Create (2480, "Sensei! Shite Ageru", "先生っ! シてあげる",
|
||||
new uint[] { 0x70562056, 0x87470744, 0x02449045, 0x76446644, 0x8F472F44 }),
|
||||
EncryptionScheme.Create (2480, "Tanetsuke Mura", "種憑け村",
|
||||
new uint[] { 0x7C7C7967, 0x7965574F, 0x4F69647C, 0x00005144, 0 }),
|
||||
EncryptionScheme.Create (2470, "You~Gaku", "よう∽ガク",
|
||||
new uint[] { 0x4DE2AB4D, 0x98AB5D46, 0x66496349, 0x485D685D, 0x5F4D4C5D }),
|
||||
EncryptionScheme.Create (2410, "Zansho Omimai Moushiagemasu", "残暑お見舞い申し上げます。",
|
||||
new uint[] { 0x3A123012, 0x6C3A6C36, 0x3C36323C, 0x6C360F16, 0x369DD012 }),
|
||||
EncryptionScheme.Create ("ShiinaRio v2.49", 2490, 0x20, EncryptionScheme.DefaultCrypt,
|
||||
new uint[] { 0x4B535453, 0xA15FA15F, 0, 0, 0 },
|
||||
"ShiinaRio4.jpg", "ShiinaRio2.png", "DecodeV249.bin"),
|
||||
};
|
||||
|
||||
58
ArcFormats/AudioEOG.cs
Normal file
58
ArcFormats/AudioEOG.cs
Normal file
@@ -0,0 +1,58 @@
|
||||
//! \file AudioEOG.cs
|
||||
//! \date Thu Jun 11 12:46:35 2015
|
||||
//! \brief Crowd engine audio file.
|
||||
//
|
||||
// Copyright (C) 2015 by morkt
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
// deal in the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
// IN THE SOFTWARE.
|
||||
//
|
||||
|
||||
using System;
|
||||
using System.ComponentModel.Composition;
|
||||
using System.IO;
|
||||
|
||||
namespace GameRes.Formats.Crowd
|
||||
{
|
||||
[Export(typeof(AudioFormat))]
|
||||
public class EogAudio : OggAudio
|
||||
{
|
||||
public override string Tag { get { return "EOG"; } }
|
||||
public override string Description { get { return "Crowd engine audio format (Ogg/Vorbis)"; } }
|
||||
public override uint Signature { get { return 0x004D5243; } } // 'CRM'
|
||||
|
||||
public override SoundInput TryOpen (Stream file)
|
||||
{
|
||||
var ogg = new StreamRegion (file, 8);
|
||||
try
|
||||
{
|
||||
return new OggInput (ogg);
|
||||
}
|
||||
catch
|
||||
{
|
||||
ogg.Dispose();
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
public override void Write (SoundInput source, Stream output)
|
||||
{
|
||||
throw new System.NotImplementedException ("EogFormat.Write not implemenented");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -25,6 +25,7 @@
|
||||
|
||||
using System.ComponentModel.Composition;
|
||||
using System.IO;
|
||||
using GameRes.Utility;
|
||||
|
||||
namespace GameRes.Formats.ShiinaRio
|
||||
{
|
||||
@@ -37,21 +38,29 @@ namespace GameRes.Formats.ShiinaRio
|
||||
|
||||
public override SoundInput TryOpen (Stream file)
|
||||
{
|
||||
var input = file as MemoryStream;
|
||||
if (null == input || !input.CanWrite)
|
||||
file.Position = 0xc;
|
||||
var header = new byte[8];
|
||||
if (8 != file.Read (header, 0, 8))
|
||||
return null;
|
||||
if (!Binary.AsciiEqual (header, 0, "fmt "))
|
||||
return null;
|
||||
uint offset = LittleEndian.ToUInt32 (header, 4);
|
||||
file.Seek (offset, SeekOrigin.Current);
|
||||
if (8 != file.Read (header, 0, 8))
|
||||
return null;
|
||||
if (!Binary.AsciiEqual (header, 0, "data"))
|
||||
return null;
|
||||
|
||||
var input = new StreamRegion (file, file.Position);
|
||||
try
|
||||
{
|
||||
input = new MemoryStream();
|
||||
file.CopyTo (input);
|
||||
return new OggInput (input);
|
||||
}
|
||||
catch
|
||||
{
|
||||
input.Dispose();
|
||||
throw;
|
||||
}
|
||||
input.Position = 1;
|
||||
input.WriteByte ((byte)'g');
|
||||
input.WriteByte ((byte)'g');
|
||||
input.WriteByte ((byte)'S');
|
||||
input.Position = 0;
|
||||
var ogg = new OggInput (input);
|
||||
if (file != input)
|
||||
file.Dispose();
|
||||
return ogg;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -109,6 +109,7 @@ namespace GameRes.Formats.Ffa
|
||||
m_data_size = LittleEndian.ToInt32 (m_input, 0x2c);
|
||||
m_output = new byte[m_data_size+0x2c];
|
||||
Buffer.BlockCopy (m_input, 4, m_output, 0, 0x2c);
|
||||
LittleEndian.Pack (m_output.Length-8, m_output, 4);
|
||||
}
|
||||
|
||||
static ushort[] word_456CA0 = new ushort[] {
|
||||
|
||||
123
ArcFormats/ImageCWL.cs
Normal file
123
ArcFormats/ImageCWL.cs
Normal file
@@ -0,0 +1,123 @@
|
||||
//! \file ImageCWL.cs
|
||||
//! \date Sat Jun 13 09:36:33 2015
|
||||
//! \brief Crowd compressed image format.
|
||||
//
|
||||
// Copyright (C) 2015 by morkt
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
// deal in the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
// IN THE SOFTWARE.
|
||||
//
|
||||
|
||||
using System;
|
||||
using System.ComponentModel.Composition;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Windows.Media;
|
||||
using GameRes.Utility;
|
||||
|
||||
namespace GameRes.Formats.Crowd
|
||||
{
|
||||
[Export(typeof(ImageFormat))]
|
||||
public class CwdFormat : ImageFormat
|
||||
{
|
||||
public override string Tag { get { return "CWD"; } }
|
||||
public override string Description { get { return "Crowd hi-color bitmap"; } }
|
||||
public override uint Signature { get { return 0x20647763u; } } // 'cwd '
|
||||
|
||||
static readonly byte[] SignatureText = Encoding.ASCII.GetBytes ("cwd format - version 1.00 -");
|
||||
|
||||
public override ImageMetaData ReadMetaData (Stream stream)
|
||||
{
|
||||
var header = new byte[0x38];
|
||||
if (header.Length != stream.Read (header, 0, header.Length))
|
||||
return null;
|
||||
if (!header.Take (SignatureText.Length).SequenceEqual (SignatureText))
|
||||
return null;
|
||||
uint key = header[0x34] + 0x259Au;
|
||||
return new ImageMetaData
|
||||
{
|
||||
Width = LittleEndian.ToUInt32 (header, 0x2c) + key,
|
||||
Height = LittleEndian.ToUInt32 (header, 0x30) + key,
|
||||
BPP = 15,
|
||||
};
|
||||
}
|
||||
|
||||
public override ImageData Read (Stream stream, ImageMetaData info)
|
||||
{
|
||||
stream.Position = 0x38;
|
||||
int size = (int)info.Width * (int)info.Height * 2;
|
||||
var pixels = new byte[size];
|
||||
if (pixels.Length != stream.Read (pixels, 0, pixels.Length))
|
||||
throw new InvalidFormatException ("Unexpected end of file");
|
||||
return ImageData.Create (info, PixelFormats.Bgr555, null, pixels);
|
||||
}
|
||||
|
||||
public override void Write (Stream file, ImageData image)
|
||||
{
|
||||
throw new NotImplementedException ("CwdFormat.Write not implemented");
|
||||
}
|
||||
}
|
||||
|
||||
[Export(typeof(ImageFormat))]
|
||||
public class CwlFormat : CwdFormat
|
||||
{
|
||||
public override string Tag { get { return "CWL"; } }
|
||||
public override string Description { get { return "LZ-compressed Crowd bitmap"; } }
|
||||
public override uint Signature { get { return 0x44445A53u; } } // 'SZDD'
|
||||
|
||||
public override ImageMetaData ReadMetaData (Stream stream)
|
||||
{
|
||||
stream.Position = 0x0e;
|
||||
using (var lz = new LzssReader (stream, 100, 0x38)) // extract CWD header
|
||||
{
|
||||
lz.FrameSize = 0x1000;
|
||||
lz.FrameFill = 0x20;
|
||||
lz.FrameInitPos = 0x1000 - 0x10;
|
||||
lz.Unpack();
|
||||
using (var cwd = new MemoryStream (lz.Data))
|
||||
return base.ReadMetaData (cwd);
|
||||
}
|
||||
}
|
||||
|
||||
public override ImageData Read (Stream stream, ImageMetaData info)
|
||||
{
|
||||
if (stream.Length > int.MaxValue)
|
||||
throw new FileSizeException();
|
||||
var header = new byte[14];
|
||||
if (header.Length != stream.Read (header, 0, header.Length))
|
||||
throw new InvalidFormatException();
|
||||
int data_length = LittleEndian.ToInt32 (header, 10);
|
||||
int input_length = (int)(stream.Length-stream.Position);
|
||||
using (var lz = new LzssReader (stream, input_length, data_length))
|
||||
{
|
||||
lz.FrameSize = 0x1000;
|
||||
lz.FrameFill = 0x20;
|
||||
lz.FrameInitPos = 0x1000 - 0x10;
|
||||
lz.Unpack();
|
||||
using (var cwd = new MemoryStream (lz.Data))
|
||||
return base.Read (cwd, info);
|
||||
}
|
||||
}
|
||||
|
||||
public override void Write (Stream file, ImageData image)
|
||||
{
|
||||
throw new NotImplementedException ("CwlFormat.Write not implemented");
|
||||
}
|
||||
}
|
||||
}
|
||||
188
ArcFormats/ImageCWP.cs
Normal file
188
ArcFormats/ImageCWP.cs
Normal file
@@ -0,0 +1,188 @@
|
||||
//! \file ImageCWP.cs
|
||||
//! \date Thu Jun 11 13:43:41 2015
|
||||
//! \brief Crowd engine image format.
|
||||
//
|
||||
// Copyright (C) 2015 by morkt
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
// deal in the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
// IN THE SOFTWARE.
|
||||
//
|
||||
|
||||
using System;
|
||||
using System.ComponentModel.Composition;
|
||||
using System.IO;
|
||||
using System.Windows.Media;
|
||||
using System.Windows.Media.Imaging;
|
||||
using GameRes.Utility;
|
||||
|
||||
namespace GameRes.Formats.Crowd
|
||||
{
|
||||
[Export(typeof(ImageFormat))]
|
||||
public class CwpFormat : ImageFormat
|
||||
{
|
||||
public override string Tag { get { return "CWP"; } }
|
||||
public override string Description { get { return "Crowd engine image format"; } }
|
||||
public override uint Signature { get { return 0x50445743; } } // 'CWDP'
|
||||
|
||||
public override ImageMetaData ReadMetaData (Stream stream)
|
||||
{
|
||||
using (var input = new ArcView.Reader (stream))
|
||||
{
|
||||
input.ReadInt32();
|
||||
uint width = Binary.BigEndian (input.ReadUInt32());
|
||||
uint height = Binary.BigEndian (input.ReadUInt32());
|
||||
if (0 == width || 0 == height)
|
||||
return null;
|
||||
int bpp = input.ReadByte();
|
||||
int color_type = input.ReadByte();
|
||||
switch (color_type)
|
||||
{
|
||||
case 2: bpp *= 3; break;
|
||||
case 4: bpp *= 2; break;
|
||||
case 6: bpp *= 4; break;
|
||||
case 3:
|
||||
case 0: break;
|
||||
default: return null;
|
||||
}
|
||||
return new ImageMetaData
|
||||
{
|
||||
Width = width,
|
||||
Height = height,
|
||||
BPP = bpp,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
public override ImageData Read (Stream stream, ImageMetaData info)
|
||||
{
|
||||
var header = new byte[0x15];
|
||||
using (var mem = new MemoryStream((int)(0x14 + stream.Length + 12)))
|
||||
using (var png = new BinaryWriter (mem))
|
||||
{
|
||||
png.Write (0x474E5089u); // png header
|
||||
png.Write (0x0A1A0A0Du);
|
||||
png.Write (0x0D000000u);
|
||||
png.Write (0x52444849u); // 'IHDR'
|
||||
stream.Position = 4;
|
||||
stream.Read (header, 0, header.Length);
|
||||
png.Write (header, 0, header.Length);
|
||||
png.Write (0x54414449u); // 'IDAT'
|
||||
stream.CopyTo (mem);
|
||||
header[1] = 0;
|
||||
header[2] = 0;
|
||||
header[3] = 0;
|
||||
LittleEndian.Pack (0x444E4549, header, 4);
|
||||
LittleEndian.Pack (0x826042AE, header, 8);
|
||||
png.Write (header, 1, 11);
|
||||
mem.Position = 0;
|
||||
var decoder = new PngBitmapDecoder (mem, BitmapCreateOptions.None, BitmapCacheOption.OnLoad);
|
||||
BitmapSource frame = decoder.Frames[0];
|
||||
var pixels = new byte[info.Width*info.Height*4];
|
||||
frame.CopyPixels (pixels, (int)info.Width*4, 0);
|
||||
for (int i = 0; i < pixels.Length; i += 4)
|
||||
{
|
||||
byte t = pixels[i];
|
||||
pixels[i] = pixels[i+2];
|
||||
pixels[i+2] = t;
|
||||
}
|
||||
return ImageData.Create (info, PixelFormats.Bgr32, null, pixels);
|
||||
}
|
||||
}
|
||||
|
||||
public override void Write (Stream file, ImageData image)
|
||||
{
|
||||
var writer = new Writer (image);
|
||||
writer.Write (file);
|
||||
}
|
||||
|
||||
internal class Writer
|
||||
{
|
||||
BitmapSource m_bitmap;
|
||||
byte[] m_buffer = new byte[81920];
|
||||
|
||||
public Writer (ImageData image)
|
||||
{
|
||||
m_bitmap = image.Bitmap;
|
||||
if (m_bitmap.Format.BitsPerPixel < 32)
|
||||
{
|
||||
m_bitmap = new FormatConvertedBitmap (m_bitmap, PixelFormats.Bgr32, null, 0);
|
||||
}
|
||||
int stride = m_bitmap.PixelWidth * 4;
|
||||
var pixels = new byte[stride*m_bitmap.PixelHeight];
|
||||
m_bitmap.CopyPixels (pixels, stride, 0);
|
||||
for (int i = 0; i < pixels.Length; i += 4)
|
||||
{
|
||||
byte t = pixels[i];
|
||||
pixels[i] = pixels[i+2];
|
||||
pixels[i+2] = t;
|
||||
}
|
||||
m_bitmap = BitmapSource.Create (m_bitmap.PixelWidth, m_bitmap.PixelHeight,
|
||||
m_bitmap.DpiX, m_bitmap.DpiY, PixelFormats.Bgra32, null, pixels, stride);
|
||||
}
|
||||
|
||||
public void Write (Stream file)
|
||||
{
|
||||
var encoder = new PngBitmapEncoder();
|
||||
encoder.Frames.Add (BitmapFrame.Create (m_bitmap));
|
||||
using (var png = new MemoryStream())
|
||||
using (var cwp = new BinaryWriter (file, System.Text.Encoding.ASCII, true))
|
||||
{
|
||||
encoder.Save (png);
|
||||
var header = new byte[0x11];
|
||||
png.Position = 0x10;
|
||||
png.Read (header, 0, header.Length);
|
||||
cwp.Write (0x50445743u); // 'CWDP'
|
||||
cwp.Write (header, 0, header.Length);
|
||||
var idat = PngFormat.FindChunk (png, "IDAT");
|
||||
if (-1 == idat)
|
||||
throw new InvalidFormatException ("CWP conversion failed");
|
||||
png.Position = idat;
|
||||
png.Read (header, 0, 8);
|
||||
int chunk_size = BigEndian.ToInt32 (header, 0) + 4;
|
||||
cwp.Write (header, 0, 4);
|
||||
for (;;)
|
||||
{
|
||||
CopyChunk (png, file, chunk_size);
|
||||
if (8 != png.Read (header, 0, 8))
|
||||
throw new InvalidFormatException ("CWP conversion failed");
|
||||
if (Binary.AsciiEqual (header, 4, "IEND"))
|
||||
{
|
||||
cwp.Write ((byte)0);
|
||||
break;
|
||||
}
|
||||
chunk_size = BigEndian.ToInt32 (header, 0) + 4;
|
||||
cwp.Write (header, 0, 8);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void CopyChunk (Stream src, Stream dst, int size)
|
||||
{
|
||||
while (size > 0)
|
||||
{
|
||||
int amount = Math.Min (size, m_buffer.Length);
|
||||
int read = src.Read (m_buffer, 0, amount);
|
||||
if (read != amount)
|
||||
throw new InvalidFormatException ("CWP conversion failed");
|
||||
dst.Write (m_buffer, 0, amount);
|
||||
size -= amount;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -428,15 +428,10 @@ namespace GameRes.Formats.DRS
|
||||
[Export(typeof(ImageFormat))]
|
||||
public class DrgIndexedFormat : ImageFormat
|
||||
{
|
||||
public override string Tag { get { return "DRG"; } }
|
||||
public override string Tag { get { return "GGD"; } }
|
||||
public override string Description { get { return "Digital Romance System indexed image format"; } }
|
||||
public override uint Signature { get { return ~0x47363532u; } } // '256G'
|
||||
|
||||
public DrgIndexedFormat ()
|
||||
{
|
||||
Extensions = new string[] { "ggd" };
|
||||
}
|
||||
|
||||
public override ImageMetaData ReadMetaData (Stream stream)
|
||||
{
|
||||
using (var input = new ArcView.Reader (stream))
|
||||
|
||||
179
ArcFormats/ImageGGP.cs
Normal file
179
ArcFormats/ImageGGP.cs
Normal file
@@ -0,0 +1,179 @@
|
||||
//! \file ImageGGP.cs
|
||||
//! \date Sun Jun 14 09:06:26 2015
|
||||
//! \brief
|
||||
//
|
||||
// Copyright (C) 2014-2015 by morkt
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
// deal in the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
// IN THE SOFTWARE.
|
||||
//
|
||||
|
||||
using System;
|
||||
using System.ComponentModel.Composition;
|
||||
using System.IO;
|
||||
using GameRes.Utility;
|
||||
|
||||
namespace GameRes.Formats.DRS
|
||||
{
|
||||
internal class GgpMetaData : ImageMetaData
|
||||
{
|
||||
public byte[] Key = new byte[8];
|
||||
public uint Offset;
|
||||
public uint Length;
|
||||
}
|
||||
|
||||
[Export(typeof(ImageFormat))]
|
||||
public class GgpFormat : PngFormat
|
||||
{
|
||||
public override string Tag { get { return "GGP"; } }
|
||||
public override string Description { get { return "Digital Romance System encrypted image format"; } }
|
||||
public override uint Signature { get { return 0x46504747u; } } // 'GGPF'
|
||||
|
||||
public override ImageMetaData ReadMetaData (Stream stream)
|
||||
{
|
||||
var header = new byte[0x24];
|
||||
if (header.Length != stream.Read (header, 0, header.Length))
|
||||
return null;
|
||||
if (!Binary.AsciiEqual (header, 0, "GGPFAIKE"))
|
||||
return null;
|
||||
var info = new GgpMetaData
|
||||
{
|
||||
Offset = LittleEndian.ToUInt32 (header, 0x14),
|
||||
Length = LittleEndian.ToUInt32 (header, 0x18),
|
||||
};
|
||||
for (int i = 0; i < 8; ++i)
|
||||
info.Key[i] = (byte)(header[i] ^ header[i+0xC]);
|
||||
stream.Position = info.Offset;
|
||||
using (var png = new EncryptedStream (stream, info.Key, true))
|
||||
{
|
||||
var png_info = base.ReadMetaData (png);
|
||||
info.Width = png_info.Width;
|
||||
info.Height = png_info.Height;
|
||||
info.BPP = png_info.BPP;
|
||||
info.OffsetX = png_info.OffsetX;
|
||||
info.OffsetY = png_info.OffsetY;
|
||||
return info;
|
||||
}
|
||||
}
|
||||
|
||||
public override ImageData Read (Stream file, ImageMetaData info)
|
||||
{
|
||||
var meta = info as GgpMetaData;
|
||||
if (null == meta)
|
||||
throw new ArgumentException ("GgpFormat.Read should be supplied with GgpMetaData", "info");
|
||||
using (var input = new StreamRegion (file, meta.Offset, meta.Length, true))
|
||||
using (var png = new EncryptedStream (input, meta.Key, true))
|
||||
return base.Read (png, info);
|
||||
}
|
||||
|
||||
public override void Write (Stream file, ImageData image)
|
||||
{
|
||||
throw new NotImplementedException ("GgpFormat.Write not implemented");
|
||||
}
|
||||
}
|
||||
|
||||
internal class EncryptedStream : Stream
|
||||
{
|
||||
private Stream m_stream;
|
||||
private byte[] m_key;
|
||||
private long m_position;
|
||||
private bool m_should_dispose;
|
||||
|
||||
public EncryptedStream (Stream main, byte[] key, bool leave_open = false)
|
||||
{
|
||||
if (null == key)
|
||||
throw new ArgumentNullException ("key");
|
||||
if (key.Length < 8)
|
||||
throw new ArgumentException ("key");
|
||||
m_stream = main;
|
||||
m_key = key;
|
||||
m_position = 0;
|
||||
m_should_dispose = !leave_open;
|
||||
}
|
||||
|
||||
public override bool CanRead { get { return m_stream.CanRead; } }
|
||||
public override bool CanSeek { get { return false; } }
|
||||
public override bool CanWrite { get { return false; } }
|
||||
public override long Length { get { throw new NotSupportedException(); } }
|
||||
public override long Position
|
||||
{
|
||||
get { throw new NotSupportedException(); }
|
||||
set { throw new NotSupportedException(); }
|
||||
}
|
||||
|
||||
public override long Seek (long offset, SeekOrigin origin)
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
public override void Flush()
|
||||
{
|
||||
m_stream.Flush();
|
||||
}
|
||||
|
||||
public override int Read (byte[] buffer, int offset, int count)
|
||||
{
|
||||
int read = m_stream.Read (buffer, offset, count);
|
||||
if (read > 0)
|
||||
{
|
||||
for (int i = 0; i < read; ++i)
|
||||
{
|
||||
buffer[offset+i] ^= m_key[m_position++ & 7];
|
||||
}
|
||||
}
|
||||
return read;
|
||||
}
|
||||
|
||||
public override int ReadByte ()
|
||||
{
|
||||
int b = m_stream.ReadByte();
|
||||
if (-1 != b)
|
||||
{
|
||||
b ^= m_key[m_position++ & 7];
|
||||
}
|
||||
return b;
|
||||
}
|
||||
|
||||
public override void SetLength (long length)
|
||||
{
|
||||
throw new NotSupportedException ("EncryptedStream.SetLength method is not supported");
|
||||
}
|
||||
|
||||
public override void Write (byte[] buffer, int offset, int count)
|
||||
{
|
||||
throw new NotSupportedException ("EncryptedStream.Write method is not supported");
|
||||
}
|
||||
|
||||
public override void WriteByte (byte value)
|
||||
{
|
||||
throw new NotSupportedException ("EncryptedStream.WriteByte method is not supported");
|
||||
}
|
||||
|
||||
bool m_disposed = false;
|
||||
protected override void Dispose (bool disposing)
|
||||
{
|
||||
if (!m_disposed)
|
||||
{
|
||||
if (m_should_dispose)
|
||||
m_stream.Dispose();
|
||||
m_disposed = true;
|
||||
base.Dispose (disposing);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -80,10 +80,7 @@ namespace GameRes.Formats.ShiinaRio
|
||||
using (var reader = new Reader (stream, meta))
|
||||
{
|
||||
var pixels = reader.Unpack();
|
||||
var bitmap = BitmapSource.Create ((int)info.Width, (int)info.Height, 96, 96,
|
||||
PixelFormats.Bgra32, null, pixels, (int)info.Width*4);
|
||||
bitmap.Freeze();
|
||||
return new ImageData (bitmap, info);
|
||||
return ImageData.Create (info, PixelFormats.Bgra32, null, pixels);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
86
ArcFormats/ImageZBM.cs
Normal file
86
ArcFormats/ImageZBM.cs
Normal file
@@ -0,0 +1,86 @@
|
||||
//! \file ImageZBM.cs
|
||||
//! \date Thu Jun 11 16:24:09 2015
|
||||
//! \brief LZ-compressed bitmap format.
|
||||
//
|
||||
// Copyright (C) 2015 by morkt
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
// deal in the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
// IN THE SOFTWARE.
|
||||
//
|
||||
|
||||
using System;
|
||||
using System.ComponentModel.Composition;
|
||||
using System.IO;
|
||||
using GameRes.Utility;
|
||||
|
||||
namespace GameRes.Formats.Crowd
|
||||
{
|
||||
[Export(typeof(ImageFormat))]
|
||||
public class ZbmFormat : BmpFormat
|
||||
{
|
||||
public override string Tag { get { return "ZBM"; } }
|
||||
public override string Description { get { return "LZ-compressed bitmap"; } }
|
||||
public override uint Signature { get { return 0x44445A53u; } } // 'SZDD'
|
||||
|
||||
public override ImageMetaData ReadMetaData (Stream stream)
|
||||
{
|
||||
stream.Position = 0x0e;
|
||||
using (var lz = new LzssReader (stream, 100, 54)) // extract BMP header
|
||||
{
|
||||
lz.FrameSize = 0x1000;
|
||||
lz.FrameFill = 0x20;
|
||||
lz.FrameInitPos = 0x1000 - 0x10;
|
||||
lz.Unpack();
|
||||
var header = lz.Data;
|
||||
for (int i = 0; i < 54; ++i)
|
||||
header[i] ^= 0xff;
|
||||
using (var bmp = new MemoryStream (header))
|
||||
return base.ReadMetaData (bmp);
|
||||
}
|
||||
}
|
||||
|
||||
public override ImageData Read (Stream stream, ImageMetaData info)
|
||||
{
|
||||
if (stream.Length > int.MaxValue)
|
||||
throw new FileSizeException();
|
||||
var header = new byte[14];
|
||||
if (header.Length != stream.Read (header, 0, header.Length))
|
||||
throw new InvalidFormatException();
|
||||
int data_length = LittleEndian.ToInt32 (header, 10);
|
||||
int input_length = (int)(stream.Length-stream.Position);
|
||||
using (var lz = new LzssReader (stream, input_length, data_length))
|
||||
{
|
||||
lz.FrameSize = 0x1000;
|
||||
lz.FrameFill = 0x20;
|
||||
lz.FrameInitPos = 0x1000 - 0x10;
|
||||
lz.Unpack();
|
||||
var data = lz.Data;
|
||||
int count = Math.Min (100, data.Length);
|
||||
for (int i = 0; i < count; ++i)
|
||||
data[i] ^= 0xff;
|
||||
using (var bmp = new MemoryStream (data))
|
||||
return base.Read (bmp, info);
|
||||
}
|
||||
}
|
||||
|
||||
public override void Write (Stream file, ImageData image)
|
||||
{
|
||||
throw new NotImplementedException ("ZbmFormat.Write not implemented");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -32,5 +32,5 @@ using System.Runtime.InteropServices;
|
||||
// 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.6.59")]
|
||||
[assembly: AssemblyFileVersion ("1.0.6.59")]
|
||||
[assembly: AssemblyVersion ("1.0.7.65")]
|
||||
[assembly: AssemblyFileVersion ("1.0.7.65")]
|
||||
|
||||
12
ArcFormats/Properties/Settings.Designer.cs
generated
12
ArcFormats/Properties/Settings.Designer.cs
generated
@@ -357,5 +357,17 @@ namespace GameRes.Formats.Properties {
|
||||
this["DPKLastScheme"] = value;
|
||||
}
|
||||
}
|
||||
|
||||
[global::System.Configuration.UserScopedSettingAttribute()]
|
||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
|
||||
[global::System.Configuration.DefaultSettingValueAttribute("")]
|
||||
public string MBLPassPhrase {
|
||||
get {
|
||||
return ((string)(this["MBLPassPhrase"]));
|
||||
}
|
||||
set {
|
||||
this["MBLPassPhrase"] = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -86,5 +86,8 @@
|
||||
<Setting Name="DPKLastScheme" Type="System.String" Scope="User">
|
||||
<Value Profile="(Default)" />
|
||||
</Setting>
|
||||
<Setting Name="MBLPassPhrase" Type="System.String" Scope="User">
|
||||
<Value Profile="(Default)" />
|
||||
</Setting>
|
||||
</Settings>
|
||||
</SettingsFile>
|
||||
9
ArcFormats/Strings/arcStrings.Designer.cs
generated
9
ArcFormats/Strings/arcStrings.Designer.cs
generated
@@ -279,6 +279,15 @@ namespace GameRes.Formats.Strings {
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Archive contains encrypted scripts.Choose encryption scheme or enter a passphrase..
|
||||
/// </summary>
|
||||
public static string MBLNotice {
|
||||
get {
|
||||
return ResourceManager.GetString("MBLNotice", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Adding file.
|
||||
/// </summary>
|
||||
|
||||
@@ -318,4 +318,8 @@ Enter archive encryption key.</value>
|
||||
<data name="KCAPDefault" xml:space="preserve">
|
||||
<value>Default</value>
|
||||
</data>
|
||||
</root>
|
||||
<data name="MBLNotice" xml:space="preserve">
|
||||
<value>Archive contains encrypted scripts.
|
||||
Choose encryption scheme or enter a passphrase.</value>
|
||||
</data>
|
||||
</root>
|
||||
|
||||
@@ -174,6 +174,10 @@
|
||||
<data name="LabelScheme" xml:space="preserve">
|
||||
<value>Вариант</value>
|
||||
</data>
|
||||
<data name="MBLNotice" xml:space="preserve">
|
||||
<value>Архив содержит зашифрованные скрипты.
|
||||
Выберите способ шифрования или введите текстовый пароль.</value>
|
||||
</data>
|
||||
<data name="MsgAddingFile" xml:space="preserve">
|
||||
<value>Добавляется файл</value>
|
||||
</data>
|
||||
|
||||
@@ -3,8 +3,7 @@
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:s="clr-namespace:GameRes.Formats.Strings"
|
||||
xmlns:p="clr-namespace:GameRes.Formats.Properties"
|
||||
xmlns:dac="clr-namespace:GameRes.Formats.Dac"
|
||||
xmlns:local="clr-namespace:GameRes.Formats.GUI">
|
||||
xmlns:dac="clr-namespace:GameRes.Formats.Dac">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
<ColumnDefinition MinWidth="130" Width="*"/>
|
||||
|
||||
17
ArcFormats/WidgetMBL.xaml
Normal file
17
ArcFormats/WidgetMBL.xaml
Normal file
@@ -0,0 +1,17 @@
|
||||
<Grid x:Class="GameRes.Formats.GUI.WidgetMBL"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:s="clr-namespace:GameRes.Formats.Strings"
|
||||
xmlns:p="clr-namespace:GameRes.Formats.Properties"
|
||||
xmlns:m="clr-namespace:GameRes.Formats.Marble">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition/>
|
||||
<RowDefinition/>
|
||||
</Grid.RowDefinitions>
|
||||
<ComboBox Name="EncScheme" Grid.Row="0" Margin="0,3,0,0" Width="160" HorizontalAlignment="Right"
|
||||
ItemsSource="{Binding Source={x:Static m:MblOpener.KnownKeys}, Mode=OneWay}"
|
||||
DisplayMemberPath="Key" SelectedValuePath="Value"
|
||||
SelectedValue="{Binding ElementName=PassPhrase, Path=Text, Mode=TwoWay}"/>
|
||||
<TextBox Name="PassPhrase" Grid.Row="1" Margin="0,3,0,3" Width="{Binding ElementName=EncScheme, Path=ActualWidth}" HorizontalAlignment="Right"
|
||||
Text="{Binding Source={x:Static p:Settings.Default}, Path=MBLPassPhrase, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
|
||||
</Grid>
|
||||
15
ArcFormats/WidgetMBL.xaml.cs
Normal file
15
ArcFormats/WidgetMBL.xaml.cs
Normal file
@@ -0,0 +1,15 @@
|
||||
using System.Windows.Controls;
|
||||
|
||||
namespace GameRes.Formats.GUI
|
||||
{
|
||||
/// <summary>
|
||||
/// Interaction logic for WidgetMBL.xaml
|
||||
/// </summary>
|
||||
public partial class WidgetMBL : Grid
|
||||
{
|
||||
public WidgetMBL ()
|
||||
{
|
||||
InitializeComponent ();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -4,9 +4,16 @@
|
||||
xmlns:fmt="clr-namespace:GameRes.Formats.ShiinaRio"
|
||||
xmlns:p="clr-namespace:GameRes.Formats.Properties"
|
||||
MaxWidth="250">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition/>
|
||||
<RowDefinition/>
|
||||
</Grid.RowDefinitions>
|
||||
<ComboBox Name="Scheme" ItemsSource="{Binding Source={x:Static fmt:Decoder.KnownSchemes}, Mode=OneWay}"
|
||||
SelectedValue="{Binding Source={x:Static p:Settings.Default}, Path=WARCScheme, Mode=TwoWay}"
|
||||
SelectedValuePath="Name"
|
||||
DisplayMemberPath="Name"
|
||||
Width="180"/>
|
||||
Width="200" Grid.Row="0"/>
|
||||
<TextBox Name="Original" Background="Transparent" BorderThickness="0" Text="{Binding Path=OriginalTitle}"
|
||||
IsReadOnly="True" TextWrapping="NoWrap" Grid.Row="1" Margin="0,3,0,3"
|
||||
DataContext="{Binding ElementName=Scheme, Path=SelectedItem}"/>
|
||||
</Grid>
|
||||
|
||||
@@ -88,6 +88,9 @@
|
||||
<setting name="DPKLastScheme" serializeAs="String">
|
||||
<value />
|
||||
</setting>
|
||||
<setting name="MBLPassPhrase" serializeAs="String">
|
||||
<value />
|
||||
</setting>
|
||||
</GameRes.Formats.Properties.Settings>
|
||||
</userSettings>
|
||||
<startup><supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5"/></startup></configuration>
|
||||
|
||||
@@ -1,28 +1,26 @@
|
||||
<Window x:Class="GARbro.GUI.ConvertImages"
|
||||
<Window x:Class="GARbro.GUI.ConvertMedia"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:s="clr-namespace:GARbro.GUI.Strings"
|
||||
xmlns:p="clr-namespace:GARbro.GUI.Properties"
|
||||
xmlns:g="clr-namespace:GameRes;assembly=GameRes"
|
||||
Title="{x:Static s:guiStrings.TextConvertImages}" ShowInTaskbar="False" WindowStartupLocation="CenterOwner"
|
||||
Title="{x:Static s:guiStrings.TextConvertMedia}" ShowInTaskbar="False" WindowStartupLocation="CenterOwner"
|
||||
ResizeMode="NoResize" SizeToContent="WidthAndHeight"
|
||||
Background="{DynamicResource {x:Static SystemColors.ControlBrushKey}}">
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition/>
|
||||
<RowDefinition/>
|
||||
<RowDefinition/>
|
||||
</Grid.RowDefinitions>
|
||||
<StackPanel Orientation="Horizontal" Grid.Row="0" Margin="20,10,20,10">
|
||||
<StackPanel Orientation="Vertical">
|
||||
<StackPanel Orientation="Horizontal" Margin="20,10,20,0">
|
||||
<Label Content="{x:Static s:guiStrings.LabelDestinationFormat}" Target="{Binding ElementName=ImageConversionFormat}" HorizontalAlignment="Left" Padding="0,0,10,0" VerticalAlignment="Center"/>
|
||||
<ComboBox Name="ImageConversionFormat" DisplayMemberPath="Tag" Width="60" HorizontalAlignment="Left"
|
||||
ItemsSource="{Binding Source={x:Static g:FormatCatalog.Instance}, Path=ImageFormats, Mode=OneWay}"
|
||||
SelectedValue="{Binding Source={x:Static p:Settings.Default}, Path=appLastImageFormat, Mode=TwoWay}" SelectedValuePath="Tag"/>
|
||||
ItemsSource="{Binding Source={x:Static g:FormatCatalog.Instance}, Path=ImageFormats, Mode=OneWay}" SelectedValuePath="Tag"
|
||||
SelectedValue="{Binding Source={x:Static p:Settings.Default}, Path=appLastImageFormat, Mode=TwoWay}"/>
|
||||
</StackPanel>
|
||||
<Separator Grid.Row="1"/>
|
||||
<StackPanel Orientation="Horizontal" Margin="10,0,10,10" HorizontalAlignment="Right" Grid.Row="2">
|
||||
<TextBlock Text="{x:Static s:guiStrings.TextAudioConversion}" Margin="20,10,20,0"/>
|
||||
<CheckBox Name="IgnoreErrors" Content="{x:Static s:guiStrings.LabelSkipFailures}" Margin="20,10"
|
||||
IsChecked="{Binding Source={x:Static p:Settings.Default}, Path=appIgnoreConversionErrors, Mode=TwoWay}"/>
|
||||
<Separator/>
|
||||
<StackPanel Orientation="Horizontal" Margin="10,0,10,10" HorizontalAlignment="Right">
|
||||
<Button Content="{x:Static s:guiStrings.ButtonOK}" Click="ConvertButton_Click" Margin="10" Width="75" IsDefault="True" Height="25"/>
|
||||
<Button Content="{x:Static s:guiStrings.ButtonCancel}" Margin="10" IsCancel="True" Width="75" Height="25"/>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</StackPanel>
|
||||
</Window>
|
||||
@@ -3,11 +3,11 @@
|
||||
namespace GARbro.GUI
|
||||
{
|
||||
/// <summary>
|
||||
/// Interaction logic for ConvertImages.xaml
|
||||
/// Interaction logic for ConvertMedia.xaml
|
||||
/// </summary>
|
||||
public partial class ConvertImages : Window
|
||||
public partial class ConvertMedia : Window
|
||||
{
|
||||
public ConvertImages ()
|
||||
public ConvertMedia ()
|
||||
{
|
||||
InitializeComponent ();
|
||||
}
|
||||
@@ -126,8 +126,8 @@
|
||||
<Compile Include="ArcParameters.xaml.cs">
|
||||
<DependentUpon>ArcParameters.xaml</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="ConvertImages.xaml.cs">
|
||||
<DependentUpon>ConvertImages.xaml</DependentUpon>
|
||||
<Compile Include="ConvertMedia.xaml.cs">
|
||||
<DependentUpon>ConvertMedia.xaml</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="CreateArchive.xaml.cs">
|
||||
<DependentUpon>CreateArchive.xaml</DependentUpon>
|
||||
@@ -168,7 +168,7 @@
|
||||
<SubType>Designer</SubType>
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
</Page>
|
||||
<Page Include="ConvertImages.xaml">
|
||||
<Page Include="ConvertMedia.xaml">
|
||||
<SubType>Designer</SubType>
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
</Page>
|
||||
|
||||
@@ -88,6 +88,11 @@ namespace GameRes
|
||||
return arc;
|
||||
}
|
||||
}
|
||||
catch (OperationCanceledException X)
|
||||
{
|
||||
FormatCatalog.Instance.LastError = X;
|
||||
return null;
|
||||
}
|
||||
catch (Exception X)
|
||||
{
|
||||
// ignore failed open attmepts
|
||||
@@ -143,6 +148,16 @@ namespace GameRes
|
||||
return m_interface.OpenEntry (this, entry);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Open specified <paramref name="entry"/> as memory-mapped view.
|
||||
/// </summary>
|
||||
public ArcView OpenView (Entry entry)
|
||||
{
|
||||
var stream = OpenEntry (entry);
|
||||
uint size = stream.CanSeek ? (uint)stream.Length : entry.Size;
|
||||
return new ArcView (stream, entry.Name, size);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Open specified <paramref name="entry"/> as a seekable Stream.
|
||||
/// </summary>
|
||||
@@ -168,6 +183,14 @@ namespace GameRes
|
||||
return ArchiveFormat.CreateFile (entry.Name);
|
||||
}
|
||||
|
||||
public IFileSystem CreateFileSystem ()
|
||||
{
|
||||
if (m_interface.IsHierarchic)
|
||||
return new ArchiveFileSystem (this);
|
||||
else
|
||||
return new FlatArchiveFileSystem (this);
|
||||
}
|
||||
|
||||
#region IDisposable Members
|
||||
bool disposed = false;
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
//! \date Mon Jul 07 10:31:10 2014
|
||||
//! \brief Memory mapped view of gameres file.
|
||||
//
|
||||
// Copyright (C) 2014 by morkt
|
||||
// Copyright (C) 2014-2015 by morkt
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
@@ -78,6 +78,8 @@ namespace GameRes
|
||||
{
|
||||
static public string ReadString (this MemoryMappedViewAccessor view, long offset, uint size, Encoding enc)
|
||||
{
|
||||
if (0 == size)
|
||||
return string.Empty;
|
||||
byte[] buffer = new byte[size];
|
||||
uint n;
|
||||
for (n = 0; n < size; ++n)
|
||||
@@ -144,14 +146,70 @@ namespace GameRes
|
||||
{
|
||||
Name = name;
|
||||
MaxOffset = fs.Length;
|
||||
m_map = MemoryMappedFile.CreateFromFile (fs, null, 0,
|
||||
MemoryMappedFileAccess.Read, null, HandleInheritability.None, true);
|
||||
try {
|
||||
View = new Frame (this);
|
||||
} catch {
|
||||
m_map.Dispose(); // dispose on error only
|
||||
throw;
|
||||
InitFromFileStream (fs, 0);
|
||||
}
|
||||
}
|
||||
|
||||
public ArcView (Stream input, string name, uint length)
|
||||
{
|
||||
Name = name;
|
||||
MaxOffset = length;
|
||||
if (input is FileStream)
|
||||
InitFromFileStream (input as FileStream, length);
|
||||
else
|
||||
InitFromStream (input, length);
|
||||
}
|
||||
|
||||
private void InitFromFileStream (FileStream fs, uint length)
|
||||
{
|
||||
m_map = MemoryMappedFile.CreateFromFile (fs, null, length,
|
||||
MemoryMappedFileAccess.Read, null, HandleInheritability.None, true);
|
||||
try {
|
||||
View = new Frame (this);
|
||||
} catch {
|
||||
m_map.Dispose(); // dispose on error only
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
private void InitFromStream (Stream input, uint length)
|
||||
{
|
||||
m_map = MemoryMappedFile.CreateNew (null, length, MemoryMappedFileAccess.ReadWrite,
|
||||
MemoryMappedFileOptions.None, null, HandleInheritability.None);
|
||||
try
|
||||
{
|
||||
using (var view = m_map.CreateViewAccessor (0, length, MemoryMappedFileAccess.Write))
|
||||
{
|
||||
var buffer = new byte[81920];
|
||||
unsafe
|
||||
{
|
||||
byte* ptr = view.GetPointer (0);
|
||||
try
|
||||
{
|
||||
uint total = 0;
|
||||
while (total < length)
|
||||
{
|
||||
int read = input.Read (buffer, 0, buffer.Length);
|
||||
if (0 == read)
|
||||
break;
|
||||
read = (int)Math.Min (read, length-total);
|
||||
Marshal.Copy (buffer, 0, (IntPtr)(ptr+total), read);
|
||||
total += (uint)read;
|
||||
}
|
||||
MaxOffset = total;
|
||||
}
|
||||
finally
|
||||
{
|
||||
view.SafeMemoryMappedViewHandle.ReleasePointer();
|
||||
}
|
||||
}
|
||||
}
|
||||
View = new Frame (this);
|
||||
}
|
||||
catch
|
||||
{
|
||||
m_map.Dispose();
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -351,7 +409,7 @@ namespace GameRes
|
||||
|
||||
public string ReadString (long offset, uint size, Encoding enc)
|
||||
{
|
||||
Reserve (offset, size);
|
||||
size = Math.Min (size, Reserve (offset, size));
|
||||
return m_view.ReadString (offset-m_offset, size, enc);
|
||||
}
|
||||
|
||||
|
||||
303
GameRes/FileSystem.cs
Normal file
303
GameRes/FileSystem.cs
Normal file
@@ -0,0 +1,303 @@
|
||||
//! \file FileSystem.cs
|
||||
//! \date Fri Jun 05 15:32:27 2015
|
||||
//! \brief Gameres file system abstraction.
|
||||
//
|
||||
// Copyright (C) 2015 by morkt
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
// deal in the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
// IN THE SOFTWARE.
|
||||
//
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
namespace GameRes
|
||||
{
|
||||
public interface IFileSystem : IDisposable
|
||||
{
|
||||
/// <summary>
|
||||
/// Open file for reading.
|
||||
/// </summary>
|
||||
Stream OpenStream (Entry entry);
|
||||
|
||||
ArcView OpenView (Entry entry);
|
||||
|
||||
/// <summary>
|
||||
/// Enumerates subdirectories and files in current directory.
|
||||
/// </summary>
|
||||
IEnumerable<Entry> GetFiles ();
|
||||
|
||||
/// <summary>
|
||||
/// Recursively enumerates files in the current directory and its subdirectories.
|
||||
/// Subdirectory entries are omitted.
|
||||
/// </summary>
|
||||
IEnumerable<Entry> GetFilesRecursive ();
|
||||
|
||||
string CurrentDirectory { get; set; }
|
||||
}
|
||||
|
||||
public class SubDirEntry : Entry
|
||||
{
|
||||
public override string Type { get { return "directory"; } }
|
||||
|
||||
public SubDirEntry (string name)
|
||||
{
|
||||
Name = name;
|
||||
Size = 0;
|
||||
}
|
||||
}
|
||||
|
||||
public class PhysicalFileSystem : IFileSystem
|
||||
{
|
||||
public string CurrentDirectory
|
||||
{
|
||||
get { return Directory.GetCurrentDirectory(); }
|
||||
set { Directory.SetCurrentDirectory (value); }
|
||||
}
|
||||
|
||||
public IEnumerable<Entry> GetFiles ()
|
||||
{
|
||||
var info = new DirectoryInfo (CurrentDirectory);
|
||||
foreach (var subdir in info.EnumerateDirectories())
|
||||
{
|
||||
if (0 != (subdir.Attributes & (FileAttributes.Hidden | FileAttributes.System)))
|
||||
continue;
|
||||
yield return new SubDirEntry (subdir.FullName);
|
||||
}
|
||||
foreach (var file in info.EnumerateFiles())
|
||||
{
|
||||
if (0 != (file.Attributes & (FileAttributes.Hidden | FileAttributes.System)))
|
||||
continue;
|
||||
yield return EntryFromFileInfo (file);
|
||||
}
|
||||
}
|
||||
|
||||
public IEnumerable<Entry> GetFilesRecursive ()
|
||||
{
|
||||
var info = new DirectoryInfo (CurrentDirectory);
|
||||
foreach (var file in info.EnumerateFiles ("*", SearchOption.AllDirectories))
|
||||
{
|
||||
if (0 != (file.Attributes & (FileAttributes.Hidden | FileAttributes.System)))
|
||||
continue;
|
||||
yield return EntryFromFileInfo (file);
|
||||
}
|
||||
}
|
||||
|
||||
private Entry EntryFromFileInfo (FileInfo file)
|
||||
{
|
||||
var entry = FormatCatalog.Instance.CreateEntry (file.FullName);
|
||||
entry.Size = (uint)Math.Min (file.Length, uint.MaxValue);
|
||||
return entry;
|
||||
}
|
||||
|
||||
public Stream OpenStream (Entry entry)
|
||||
{
|
||||
return File.OpenRead (entry.Name);
|
||||
}
|
||||
|
||||
public ArcView OpenView (Entry entry)
|
||||
{
|
||||
return new ArcView (entry.Name);
|
||||
}
|
||||
|
||||
public void Dispose ()
|
||||
{
|
||||
GC.SuppressFinalize (this);
|
||||
}
|
||||
}
|
||||
|
||||
public class FlatArchiveFileSystem : IFileSystem
|
||||
{
|
||||
protected ArcFile m_arc;
|
||||
|
||||
public virtual string CurrentDirectory
|
||||
{
|
||||
get { return ""; }
|
||||
set
|
||||
{
|
||||
if (string.IsNullOrEmpty (value))
|
||||
return;
|
||||
if (".." == value || "." == value)
|
||||
return;
|
||||
if ("\\" == value || "/" == value)
|
||||
return;
|
||||
throw new DirectoryNotFoundException();
|
||||
}
|
||||
}
|
||||
|
||||
public FlatArchiveFileSystem (ArcFile arc)
|
||||
{
|
||||
m_arc = arc;
|
||||
}
|
||||
|
||||
public Stream OpenStream (Entry entry)
|
||||
{
|
||||
return m_arc.OpenEntry (entry);
|
||||
}
|
||||
|
||||
public ArcView OpenView (Entry entry)
|
||||
{
|
||||
return m_arc.OpenView (entry);
|
||||
}
|
||||
|
||||
public virtual IEnumerable<Entry> GetFiles ()
|
||||
{
|
||||
return m_arc.Dir;
|
||||
}
|
||||
|
||||
public virtual IEnumerable<Entry> GetFilesRecursive ()
|
||||
{
|
||||
return m_arc.Dir;
|
||||
}
|
||||
|
||||
#region IDisposable Members
|
||||
bool _arc_disposed = false;
|
||||
|
||||
public void Dispose ()
|
||||
{
|
||||
Dispose (true);
|
||||
GC.SuppressFinalize (this);
|
||||
}
|
||||
|
||||
protected virtual void Dispose (bool disposing)
|
||||
{
|
||||
if (!_arc_disposed)
|
||||
{
|
||||
if (disposing)
|
||||
{
|
||||
m_arc.Dispose();
|
||||
}
|
||||
_arc_disposed = true;
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
|
||||
public class ArchiveFileSystem : FlatArchiveFileSystem
|
||||
{
|
||||
private string m_cwd;
|
||||
|
||||
private string PathDelimiter { get; set; }
|
||||
|
||||
private static readonly char[] m_path_delimiters = { '/', '\\' };
|
||||
|
||||
public ArchiveFileSystem (ArcFile arc) : base (arc)
|
||||
{
|
||||
m_cwd = "";
|
||||
PathDelimiter = "/";
|
||||
}
|
||||
|
||||
public override string CurrentDirectory
|
||||
{
|
||||
get { return m_cwd; }
|
||||
set { ChDir (value); }
|
||||
}
|
||||
|
||||
static readonly Regex path_re = new Regex (@"\G[/\\]?([^/\\]+)([/\\])");
|
||||
|
||||
public override IEnumerable<Entry> GetFiles ()
|
||||
{
|
||||
var root_dir = "";
|
||||
IEnumerable<Entry> dir = m_arc.Dir;
|
||||
if (!string.IsNullOrEmpty (m_cwd))
|
||||
{
|
||||
root_dir = m_cwd+PathDelimiter;
|
||||
dir = from entry in dir
|
||||
where entry.Name.StartsWith (root_dir)
|
||||
select entry;
|
||||
if (!dir.Any())
|
||||
{
|
||||
throw new DirectoryNotFoundException();
|
||||
}
|
||||
}
|
||||
var subdirs = new HashSet<string>();
|
||||
foreach (var entry in dir)
|
||||
{
|
||||
var match = path_re.Match (entry.Name, root_dir.Length);
|
||||
if (match.Success)
|
||||
{
|
||||
string name = match.Groups[1].Value;
|
||||
if (subdirs.Add (name))
|
||||
{
|
||||
PathDelimiter = match.Groups[2].Value;
|
||||
yield return new SubDirEntry (root_dir+name);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
yield return entry;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override IEnumerable<Entry> GetFilesRecursive ()
|
||||
{
|
||||
if (0 == m_cwd.Length)
|
||||
return m_arc.Dir;
|
||||
else
|
||||
return from file in m_arc.Dir
|
||||
where file.Name.StartsWith (m_cwd + PathDelimiter)
|
||||
select file;
|
||||
}
|
||||
|
||||
private void ChDir (string path)
|
||||
{
|
||||
if (string.IsNullOrEmpty (path))
|
||||
return;
|
||||
List<string> cur_dir;
|
||||
if (-1 != Array.IndexOf (m_path_delimiters, path[0]))
|
||||
{
|
||||
path = path.TrimStart (m_path_delimiters);
|
||||
cur_dir = new List<string>();
|
||||
}
|
||||
else
|
||||
{
|
||||
cur_dir = m_cwd.Split (m_path_delimiters).ToList();
|
||||
}
|
||||
var path_list = path.Split (m_path_delimiters);
|
||||
foreach (var dir in path_list)
|
||||
{
|
||||
if ("." == dir)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
else if (".." == dir)
|
||||
{
|
||||
if (0 == cur_dir.Count)
|
||||
continue;
|
||||
cur_dir.RemoveAt (cur_dir.Count-1);
|
||||
}
|
||||
else
|
||||
{
|
||||
cur_dir.Add (dir);
|
||||
}
|
||||
}
|
||||
string new_path = string.Join (PathDelimiter, cur_dir);
|
||||
if (0 != new_path.Length)
|
||||
{
|
||||
var entry = m_arc.Dir.FirstOrDefault (e => e.Name.StartsWith (new_path + PathDelimiter));
|
||||
if (null == entry)
|
||||
throw new DirectoryNotFoundException();
|
||||
}
|
||||
m_cwd = new_path;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -57,6 +57,7 @@
|
||||
<Compile Include="ArcView.cs" />
|
||||
<Compile Include="Audio.cs" />
|
||||
<Compile Include="AudioWAV.cs" />
|
||||
<Compile Include="FileSystem.cs" />
|
||||
<Compile Include="GameRes.cs" />
|
||||
<Compile Include="Image.cs" />
|
||||
<Compile Include="ImageBMP.cs" />
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
//! \date Sat Jul 05 00:09:15 2014
|
||||
//! \brief PNG image implementation.
|
||||
//
|
||||
// Copyright (C) 2014 by morkt
|
||||
// Copyright (C) 2014-2015 by morkt
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
@@ -120,8 +120,7 @@ namespace GameRes
|
||||
var file = new ArcView.Reader (stream);
|
||||
try
|
||||
{
|
||||
if (file.ReadUInt32() != Signature)
|
||||
return null;
|
||||
file.ReadUInt32();
|
||||
if (file.ReadUInt32() != 0x0a1a0a0d)
|
||||
return null;
|
||||
uint chunk_size = Binary.BigEndian (file.ReadUInt32());
|
||||
@@ -178,9 +177,8 @@ namespace GameRes
|
||||
return meta;
|
||||
}
|
||||
|
||||
long FindChunk (Stream stream, string chunk)
|
||||
public static long FindChunk (Stream stream, string chunk)
|
||||
{
|
||||
char[] find_name = chunk.ToCharArray();
|
||||
long found_offset = -1;
|
||||
var file = new ArcView.Reader (stream);
|
||||
try
|
||||
|
||||
@@ -32,5 +32,5 @@ using System.Runtime.InteropServices;
|
||||
// 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.5.6")]
|
||||
[assembly: AssemblyFileVersion ("1.0.5.6")]
|
||||
[assembly: AssemblyVersion ("1.1.1.8")]
|
||||
[assembly: AssemblyFileVersion ("1.1.1.8")]
|
||||
|
||||
151
GarConvert.cs
151
GarConvert.cs
@@ -2,6 +2,26 @@
|
||||
//! \date Fri Aug 22 08:22:47 2014
|
||||
//! \brief Game resources conversion methods.
|
||||
//
|
||||
// Copyright (C) 2014-2015 by morkt
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to
|
||||
// deal in the Software without restriction, including without limitation the
|
||||
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
// sell copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
// IN THE SOFTWARE.
|
||||
//
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
@@ -23,53 +43,61 @@ namespace GARbro.GUI
|
||||
/// <summary>
|
||||
/// Convert selected images to another format.
|
||||
/// </summary>
|
||||
void ConvertImageExec (object sender, ExecutedRoutedEventArgs e)
|
||||
void ConvertMediaExec (object sender, ExecutedRoutedEventArgs e)
|
||||
{
|
||||
if (ViewModel.IsArchive)
|
||||
return;
|
||||
var source = CurrentDirectory.SelectedItems.Cast<EntryViewModel>()
|
||||
.Where (f => f.Type == "image").Select (f => f.Name);
|
||||
var convert_dialog = new ConvertImages();
|
||||
.Where (f => f.Type == "image" || f.Type == "audio")
|
||||
.Select (f => f.Source);
|
||||
if (!source.Any())
|
||||
{
|
||||
PopupError (guiStrings.MsgNoMediaFiles, guiStrings.TextMediaConvertError);
|
||||
return;
|
||||
}
|
||||
var convert_dialog = new ConvertMedia();
|
||||
convert_dialog.Owner = this;
|
||||
var result = convert_dialog.ShowDialog() ?? false;
|
||||
if (!result)
|
||||
return;
|
||||
var selected = convert_dialog.ImageConversionFormat.SelectedValue as string;
|
||||
var format = FormatCatalog.Instance.ImageFormats.FirstOrDefault (f => f.Tag == selected);
|
||||
var format = convert_dialog.ImageConversionFormat.SelectedItem as ImageFormat;
|
||||
if (null == format)
|
||||
{
|
||||
Trace.WriteLine ("Format is not selected", "ConvertImageExec");
|
||||
Trace.WriteLine ("Format is not selected", "ConvertMediaExec");
|
||||
return;
|
||||
}
|
||||
try
|
||||
{
|
||||
Directory.SetCurrentDirectory (ViewModel.Path);
|
||||
var converter = new GarConvertImages (this);
|
||||
var converter = new GarConvertMedia (this);
|
||||
converter.IgnoreErrors = convert_dialog.IgnoreErrors.IsChecked ?? false;
|
||||
converter.Convert (source, format);
|
||||
}
|
||||
catch (Exception X)
|
||||
{
|
||||
PopupError (X.Message, guiStrings.TextImageConvertError);
|
||||
PopupError (X.Message, guiStrings.TextMediaConvertError);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal class GarConvertImages
|
||||
internal class GarConvertMedia
|
||||
{
|
||||
private MainWindow m_main;
|
||||
private ProgressDialog m_progress_dialog;
|
||||
private IEnumerable<string> m_source;
|
||||
private IEnumerable<Entry> m_source;
|
||||
private ImageFormat m_image_format;
|
||||
private Exception m_pending_error;
|
||||
private List<Tuple<string,string>> m_failed = new List<Tuple<string,string>>();
|
||||
|
||||
public bool IgnoreErrors { get; set; }
|
||||
public IEnumerable<Tuple<string,string>> FailedFiles { get { return m_failed; } }
|
||||
|
||||
public GarConvertImages (MainWindow parent)
|
||||
public GarConvertMedia (MainWindow parent)
|
||||
{
|
||||
m_main = parent;
|
||||
}
|
||||
|
||||
public void Convert (IEnumerable<string> images, ImageFormat format)
|
||||
public void Convert (IEnumerable<Entry> images, ImageFormat format)
|
||||
{
|
||||
m_main.StopWatchDirectoryChanges();
|
||||
m_source = images;
|
||||
@@ -91,46 +119,28 @@ namespace GARbro.GUI
|
||||
m_pending_error = null;
|
||||
try
|
||||
{
|
||||
string target_ext = m_image_format.Extensions.First();
|
||||
int total = m_source.Count();
|
||||
int i = 0;
|
||||
foreach (var filename in m_source)
|
||||
foreach (var entry in m_source)
|
||||
{
|
||||
if (m_progress_dialog.CancellationPending)
|
||||
throw new OperationCanceledException();
|
||||
var filename = entry.Name;
|
||||
int progress = i++*100/total;
|
||||
string target_name = Path.ChangeExtension (filename, target_ext);
|
||||
if (filename == target_name)
|
||||
continue;
|
||||
string source_ext = Path.GetExtension (filename).TrimStart ('.').ToLowerInvariant();
|
||||
if (m_image_format.Extensions.Any (ext => ext == source_ext))
|
||||
continue;
|
||||
m_progress_dialog.ReportProgress (progress, string.Format (guiStrings.MsgConvertingFile,
|
||||
Path.GetFileName (filename)), null);
|
||||
try
|
||||
{
|
||||
using (var file = File.OpenRead (filename))
|
||||
{
|
||||
m_progress_dialog.ReportProgress (progress, string.Format (guiStrings.MsgConvertingImage,
|
||||
filename), target_name);
|
||||
var image = ImageFormat.Read (file);
|
||||
if (null == image)
|
||||
continue;
|
||||
try
|
||||
{
|
||||
using (var output = File.Create (target_name))
|
||||
m_image_format.Write (output, image);
|
||||
}
|
||||
catch // delete destination file on conversion failure
|
||||
{
|
||||
File.Delete (target_name);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
if ("image" == entry.Type)
|
||||
ConvertImage (filename);
|
||||
else if ("audio" == entry.Type)
|
||||
ConvertAudio (filename);
|
||||
}
|
||||
catch (Exception X)
|
||||
{
|
||||
if (!IgnoreErrors)
|
||||
throw;
|
||||
m_pending_error = X;
|
||||
m_failed.Add (Tuple.Create (Path.GetFileName (filename), X.Message));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -140,6 +150,67 @@ namespace GARbro.GUI
|
||||
}
|
||||
}
|
||||
|
||||
public static readonly HashSet<string> CommonAudioFormats = new HashSet<string> { "wav", "mp3", "ogg" };
|
||||
public static readonly AudioFormat WavFormat = FormatCatalog.Instance.AudioFormats.First (f => f.Tag == "WAV");
|
||||
|
||||
void ConvertAudio (string filename)
|
||||
{
|
||||
using (var file = File.OpenRead (filename))
|
||||
using (var input = AudioFormat.Read (file))
|
||||
{
|
||||
if (null == input)
|
||||
return;
|
||||
var source_ext = Path.GetExtension (filename).TrimStart ('.').ToLowerInvariant();
|
||||
string source_format = input.SourceFormat;
|
||||
if (CommonAudioFormats.Contains (source_format))
|
||||
{
|
||||
if (source_ext == source_format)
|
||||
return;
|
||||
string output_name = Path.ChangeExtension (filename, source_format);
|
||||
using (var output = File.Create (output_name))
|
||||
{
|
||||
input.Source.Position = 0;
|
||||
input.Source.CopyTo (output);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (source_ext == "wav")
|
||||
return;
|
||||
string output_name = Path.ChangeExtension (filename, "wav");
|
||||
using (var output = File.Create (output_name))
|
||||
WavFormat.Write (input, output);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ConvertImage (string filename)
|
||||
{
|
||||
string target_ext = m_image_format.Extensions.First();
|
||||
string target_name = Path.ChangeExtension (filename, target_ext);
|
||||
if (filename == target_name)
|
||||
return;
|
||||
string source_ext = Path.GetExtension (filename).TrimStart ('.').ToLowerInvariant();
|
||||
if (m_image_format.Extensions.Any (ext => ext == source_ext))
|
||||
return;
|
||||
using (var file = File.OpenRead (filename))
|
||||
{
|
||||
var image = ImageFormat.Read (file);
|
||||
if (null == image)
|
||||
return;
|
||||
try
|
||||
{
|
||||
using (var output = File.Create (target_name))
|
||||
m_image_format.Write (output, image);
|
||||
}
|
||||
catch // delete destination file on conversion failure
|
||||
{
|
||||
File.Delete (target_name);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void OnConvertComplete (object sender, RunWorkerCompletedEventArgs e)
|
||||
{
|
||||
m_main.ResumeWatchDirectoryChanges();
|
||||
@@ -149,7 +220,7 @@ namespace GARbro.GUI
|
||||
if (m_pending_error is OperationCanceledException)
|
||||
m_main.SetStatusText (m_pending_error.Message);
|
||||
else
|
||||
m_main.PopupError (m_pending_error.Message, guiStrings.TextImageConvertError);
|
||||
m_main.PopupError (m_pending_error.Message, guiStrings.TextMediaConvertError);
|
||||
}
|
||||
m_main.Activate();
|
||||
m_main.RefreshView();
|
||||
|
||||
@@ -31,6 +31,7 @@ using System.ComponentModel;
|
||||
using System.Diagnostics;
|
||||
using System.Windows;
|
||||
using System.Windows.Input;
|
||||
using System.Windows.Media.Imaging;
|
||||
using Ookii.Dialogs.Wpf;
|
||||
using GameRes;
|
||||
using GARbro.GUI.Strings;
|
||||
@@ -104,6 +105,7 @@ namespace GARbro.GUI
|
||||
private bool m_skip_images = false;
|
||||
private bool m_skip_script = false;
|
||||
private bool m_skip_audio = false;
|
||||
private bool m_adjust_image_offset = false;
|
||||
private bool m_convert_audio;
|
||||
private ImageFormat m_image_format;
|
||||
private int m_extract_count;
|
||||
@@ -111,8 +113,6 @@ namespace GARbro.GUI
|
||||
private ProgressDialog m_progress_dialog;
|
||||
private Exception m_pending_error;
|
||||
|
||||
public static readonly HashSet<string> CommonAudioFormats = new HashSet<string> { "wav", "mp3", "ogg" };
|
||||
|
||||
public bool IsActive { get { return m_extract_in_progress; } }
|
||||
|
||||
public GarExtract (MainWindow parent, string source)
|
||||
@@ -290,13 +290,13 @@ namespace GARbro.GUI
|
||||
}
|
||||
}
|
||||
|
||||
static void ExtractImage (ArcFile arc, Entry entry, ImageFormat target_format)
|
||||
void ExtractImage (ArcFile arc, Entry entry, ImageFormat target_format)
|
||||
{
|
||||
using (var file = arc.OpenSeekableEntry (entry))
|
||||
{
|
||||
var src_format = ImageFormat.FindFormat (file);
|
||||
if (null == src_format)
|
||||
throw new InvalidFormatException (string.Format ("{1}: {0}", guiStrings.MsgUnableInterpret, entry.Name));
|
||||
throw new InvalidFormatException (string.Format ("{1}: {0}", guiStrings.MsgUnableInterpretImage, entry.Name));
|
||||
file.Position = 0;
|
||||
string target_ext = target_format.Extensions.First();
|
||||
string outname = Path.ChangeExtension (entry.Name, target_ext);
|
||||
@@ -309,6 +309,10 @@ namespace GARbro.GUI
|
||||
}
|
||||
ImageData image = src_format.Item1.Read (file, src_format.Item2);
|
||||
Trace.WriteLine (string.Format ("{0} => {1}", entry.Name, outname), "ExtractImage");
|
||||
if (m_adjust_image_offset)
|
||||
{
|
||||
image = AdjustImageOffset (image);
|
||||
}
|
||||
using (var outfile = ArchiveFormat.CreateFile (outname))
|
||||
{
|
||||
target_format.Write (outfile, image);
|
||||
@@ -316,13 +320,42 @@ namespace GARbro.GUI
|
||||
}
|
||||
}
|
||||
|
||||
static ImageData AdjustImageOffset (ImageData image)
|
||||
{
|
||||
if (0 == image.OffsetX && 0 == image.OffsetY)
|
||||
return image;
|
||||
int width = (int)image.Width + image.OffsetX;
|
||||
int height = (int)image.Height + image.OffsetY;
|
||||
if (width <= 0 || height <= 0)
|
||||
return image;
|
||||
|
||||
int x = Math.Max (image.OffsetX, 0);
|
||||
int y = Math.Max (image.OffsetY, 0);
|
||||
int src_x = image.OffsetX < 0 ? Math.Abs (image.OffsetX) : 0;
|
||||
int src_y = image.OffsetY < 0 ? Math.Abs (image.OffsetY) : 0;
|
||||
int src_stride = (int)image.Width * (image.BPP+7) / 8;
|
||||
int dst_stride = width * (image.BPP+7) / 8;
|
||||
var pixels = new byte[height*dst_stride];
|
||||
int offset = y * dst_stride + x * image.BPP / 8;
|
||||
Int32Rect rect = new Int32Rect (src_x, src_y, (int)image.Width - src_x, 1);
|
||||
for (int row = src_y; row < image.Height; ++row)
|
||||
{
|
||||
rect.Y = row;
|
||||
image.Bitmap.CopyPixels (rect, pixels, src_stride, offset);
|
||||
offset += dst_stride;
|
||||
}
|
||||
var bitmap = BitmapSource.Create (width, height, image.Bitmap.DpiX, image.Bitmap.DpiY,
|
||||
image.Bitmap.Format, image.Bitmap.Palette, pixels, dst_stride);
|
||||
return new ImageData (bitmap);
|
||||
}
|
||||
|
||||
static void ExtractAudio (ArcFile arc, Entry entry)
|
||||
{
|
||||
using (var file = arc.OpenEntry (entry))
|
||||
using (var sound = AudioFormat.Read (file))
|
||||
{
|
||||
if (null == sound)
|
||||
throw new InvalidFormatException (string.Format ("{1}: {0}", guiStrings.MsgUnableInterpret, entry.Name));
|
||||
throw new InvalidFormatException (string.Format ("{1}: {0}", guiStrings.MsgUnableInterpretAudio, entry.Name));
|
||||
ConvertAudio (entry.Name, sound);
|
||||
}
|
||||
}
|
||||
@@ -330,7 +363,7 @@ namespace GARbro.GUI
|
||||
public static void ConvertAudio (string entry_name, SoundInput input)
|
||||
{
|
||||
string source_format = input.SourceFormat;
|
||||
if (CommonAudioFormats.Contains (source_format))
|
||||
if (GarConvertMedia.CommonAudioFormats.Contains (source_format))
|
||||
{
|
||||
string output_name = Path.ChangeExtension (entry_name, source_format);
|
||||
using (var output = ArchiveFormat.CreateFile (output_name))
|
||||
@@ -341,10 +374,9 @@ namespace GARbro.GUI
|
||||
}
|
||||
else
|
||||
{
|
||||
var wav_format = FormatCatalog.Instance.AudioFormats.First (f => f.Tag == "WAV");
|
||||
string output_name = Path.ChangeExtension (entry_name, "wav");
|
||||
using (var output = ArchiveFormat.CreateFile (output_name))
|
||||
wav_format.Write (input, output);
|
||||
GarConvertMedia.WavFormat.Write (input, output);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -200,15 +200,7 @@ namespace GARbro.GUI
|
||||
}
|
||||
else
|
||||
{
|
||||
var file = preview.Archive.OpenEntry (preview.Entry);
|
||||
if (file.CanSeek)
|
||||
return file;
|
||||
using (file)
|
||||
{
|
||||
var memory = new MemoryStream();
|
||||
file.CopyTo (memory);
|
||||
return memory;
|
||||
}
|
||||
return preview.Archive.OpenSeekableEntry (preview.Entry);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -94,7 +94,7 @@
|
||||
Command="{x:Static local:Commands.Refresh}"/>
|
||||
<MenuItem Header="{x:Static s:guiStrings.CtxMenuConvert}" InputGestureText="F6"
|
||||
Visibility="{Binding Path=IsEnabled, RelativeSource={RelativeSource Self}, Mode=OneWay, Converter={StaticResource booleanToCollapsedVisibilityConverter}}"
|
||||
Command="{x:Static local:Commands.ConvertImage}" />
|
||||
Command="{x:Static local:Commands.ConvertMedia}" />
|
||||
</ContextMenu>
|
||||
|
||||
</Window.Resources>
|
||||
@@ -107,7 +107,7 @@
|
||||
<MenuItem Header="{x:Static s:guiStrings.CtxMenuCreate}" InputGestureText="F3"
|
||||
Command="{x:Static local:Commands.CreateArchive}" />
|
||||
<MenuItem Header="{x:Static s:guiStrings.CtxMenuConvert}" InputGestureText="F6"
|
||||
Command="{x:Static local:Commands.ConvertImage}" />
|
||||
Command="{x:Static local:Commands.ConvertMedia}" />
|
||||
<MenuItem Header="{x:Static s:guiStrings.MenuRecent}" x:Name="RecentFilesMenu">
|
||||
<MenuItem.ItemContainerStyle>
|
||||
<Style TargetType="MenuItem">
|
||||
@@ -265,7 +265,7 @@
|
||||
<KeyBinding Gesture="F2" Command="{x:Static local:Commands.RenameItem}"/>
|
||||
<KeyBinding Gesture="F4" Command="{x:Static local:Commands.ExtractItem}"/>
|
||||
<KeyBinding Gesture="F5" Command="{x:Static local:Commands.Refresh}"/>
|
||||
<KeyBinding Gesture="F6" Command="{x:Static local:Commands.ConvertImage}"/>
|
||||
<KeyBinding Gesture="F6" Command="{x:Static local:Commands.ConvertMedia}"/>
|
||||
<KeyBinding Gesture="Delete" Command="{x:Static local:Commands.DeleteItem}"/>
|
||||
<KeyBinding Gesture="Backspace" Command="{x:Static local:Commands.GoBack}"/>
|
||||
<MouseBinding Gesture="LeftDoubleClick" Command="{x:Static local:Commands.OpenItem}" />
|
||||
@@ -366,7 +366,7 @@
|
||||
<CommandBinding Command="{x:Static local:Commands.DeleteItem}" Executed="DeleteItemExec" CanExecute="CanExecuteOnPhysicalFile" />
|
||||
<CommandBinding Command="{x:Static local:Commands.RenameItem}" Executed="RenameItemExec" CanExecute="CanExecuteInDirectory" />
|
||||
<CommandBinding Command="{x:Static local:Commands.ExploreItem}" Executed="ExploreItemExec" CanExecute="CanExecuteInDirectory" />
|
||||
<CommandBinding Command="{x:Static local:Commands.ConvertImage}" Executed="ConvertImageExec" CanExecute="CanExecuteConvertImage" />
|
||||
<CommandBinding Command="{x:Static local:Commands.ConvertMedia}" Executed="ConvertMediaExec" CanExecute="CanExecuteConvertMedia" />
|
||||
<CommandBinding Command="{x:Static local:Commands.SortBy}" Executed="SortByExec" CanExecute="CanExecuteAlways"/>
|
||||
<CommandBinding Command="{x:Static local:Commands.GoBack}" Executed="GoBackExec" CanExecute="CanExecuteGoBack"/>
|
||||
<CommandBinding Command="{x:Static local:Commands.GoForward}" Executed="GoForwardExec" CanExecute="CanExecuteGoForward"/>
|
||||
|
||||
@@ -120,6 +120,7 @@ namespace GARbro.GUI
|
||||
/// </summary>
|
||||
protected override void OnClosing (CancelEventArgs e)
|
||||
{
|
||||
AudioDevice = null;
|
||||
CurrentAudio = null;
|
||||
SaveSettings();
|
||||
base.OnClosing (e);
|
||||
@@ -811,27 +812,40 @@ namespace GARbro.GUI
|
||||
return File.OpenRead (Path.Combine (vm.Path, entry.Name));
|
||||
}
|
||||
|
||||
WaveOutEvent m_audio;
|
||||
WaveOutEvent CurrentAudio
|
||||
WaveOutEvent m_audio_device;
|
||||
WaveOutEvent AudioDevice
|
||||
{
|
||||
get { return m_audio; }
|
||||
get { return m_audio_device; }
|
||||
set
|
||||
{
|
||||
if (m_audio != null)
|
||||
m_audio.Dispose();
|
||||
m_audio = value;
|
||||
if (m_audio_device != null)
|
||||
m_audio_device.Dispose();
|
||||
m_audio_device = value;
|
||||
}
|
||||
}
|
||||
|
||||
WaveStream m_audio_input;
|
||||
WaveStream CurrentAudio
|
||||
{
|
||||
get { return m_audio_input; }
|
||||
set
|
||||
{
|
||||
if (m_audio_input != null)
|
||||
m_audio_input.Dispose();
|
||||
m_audio_input = value;
|
||||
}
|
||||
}
|
||||
|
||||
private void PlayFile (Entry entry)
|
||||
{
|
||||
SoundInput sound = null;
|
||||
try
|
||||
{
|
||||
SetBusyState();
|
||||
using (var input = OpenEntry (entry))
|
||||
{
|
||||
FormatCatalog.Instance.LastError = null;
|
||||
var sound = AudioFormat.Read (input);
|
||||
sound = AudioFormat.Read (input);
|
||||
if (null == sound)
|
||||
{
|
||||
if (null != FormatCatalog.Instance.LastError)
|
||||
@@ -839,30 +853,34 @@ namespace GARbro.GUI
|
||||
return;
|
||||
}
|
||||
|
||||
if (CurrentAudio != null)
|
||||
if (AudioDevice != null)
|
||||
{
|
||||
CurrentAudio.PlaybackStopped -= OnPlaybackStopped;
|
||||
CurrentAudio = null;
|
||||
AudioDevice.PlaybackStopped -= OnPlaybackStopped;
|
||||
AudioDevice = null;
|
||||
}
|
||||
var wave_stream = new WaveStreamImpl (sound);
|
||||
CurrentAudio = new WaveOutEvent();
|
||||
CurrentAudio.Init (wave_stream);
|
||||
CurrentAudio.PlaybackStopped += OnPlaybackStopped;
|
||||
CurrentAudio.Play();
|
||||
var fmt = wave_stream.WaveFormat;
|
||||
SetResourceText (string.Format ("Playing {0} / {2}bps / {1}Hz", entry.Name,
|
||||
fmt.SampleRate, sound.SourceBitrate / 1000));
|
||||
CurrentAudio = new WaveStreamImpl (sound);
|
||||
AudioDevice = new WaveOutEvent();
|
||||
AudioDevice.Init (CurrentAudio);
|
||||
AudioDevice.PlaybackStopped += OnPlaybackStopped;
|
||||
AudioDevice.Play();
|
||||
var fmt = CurrentAudio.WaveFormat;
|
||||
SetResourceText (string.Format ("Playing {0} / {3} / {2}bps / {1}Hz", entry.Name,
|
||||
fmt.SampleRate, sound.SourceBitrate / 1000,
|
||||
CurrentAudio.TotalTime.ToString ("m':'ss")));
|
||||
}
|
||||
}
|
||||
catch (Exception X)
|
||||
{
|
||||
SetStatusText (X.Message);
|
||||
if (null != sound)
|
||||
sound.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
private void OnPlaybackStopped (object sender, StoppedEventArgs e)
|
||||
{
|
||||
SetResourceText ("");
|
||||
CurrentAudio = null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -930,6 +948,7 @@ namespace GARbro.GUI
|
||||
try
|
||||
{
|
||||
m_app.ResetCache();
|
||||
ResetPreviewPane();
|
||||
if (!items.Skip (1).Any()) // items.Count() == 1
|
||||
{
|
||||
string item_name = Path.Combine (CurrentPath, items.First().Name);
|
||||
@@ -1056,16 +1075,12 @@ namespace GARbro.GUI
|
||||
e.CanExecute = CurrentDirectory.SelectedIndex != -1;
|
||||
}
|
||||
|
||||
private void CanExecuteConvertImage (object sender, CanExecuteRoutedEventArgs e)
|
||||
private void CanExecuteConvertMedia (object sender, CanExecuteRoutedEventArgs e)
|
||||
{
|
||||
if (CurrentDirectory.SelectedItems.Count > 1)
|
||||
if (CurrentDirectory.SelectedItems.Count >= 1)
|
||||
{
|
||||
e.CanExecute = !ViewModel.IsArchive;
|
||||
}
|
||||
else
|
||||
{
|
||||
CanExecuteOnImage (sender, e);
|
||||
}
|
||||
}
|
||||
|
||||
private void CanExecuteOnImage (object sender, CanExecuteRoutedEventArgs e)
|
||||
@@ -1283,7 +1298,7 @@ namespace GARbro.GUI
|
||||
public static readonly RoutedCommand DeleteItem = new RoutedCommand();
|
||||
public static readonly RoutedCommand RenameItem = new RoutedCommand();
|
||||
public static readonly RoutedCommand ExploreItem = new RoutedCommand();
|
||||
public static readonly RoutedCommand ConvertImage = new RoutedCommand();
|
||||
public static readonly RoutedCommand ConvertMedia = new RoutedCommand();
|
||||
public static readonly RoutedCommand Refresh = new RoutedCommand();
|
||||
public static readonly RoutedCommand Browse = new RoutedCommand();
|
||||
public static readonly RoutedCommand FitWindow = new RoutedCommand();
|
||||
|
||||
@@ -51,5 +51,5 @@ using System.Windows;
|
||||
// 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.1.2.20")]
|
||||
[assembly: AssemblyFileVersion ("1.1.2.20")]
|
||||
[assembly: AssemblyVersion ("1.1.3.21")]
|
||||
[assembly: AssemblyFileVersion ("1.1.3.21")]
|
||||
|
||||
12
Properties/Settings.Designer.cs
generated
12
Properties/Settings.Designer.cs
generated
@@ -333,5 +333,17 @@ namespace GARbro.GUI.Properties {
|
||||
this["appExtractAudio"] = value;
|
||||
}
|
||||
}
|
||||
|
||||
[global::System.Configuration.UserScopedSettingAttribute()]
|
||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
|
||||
[global::System.Configuration.DefaultSettingValueAttribute("False")]
|
||||
public bool appIgnoreConversionErrors {
|
||||
get {
|
||||
return ((bool)(this["appIgnoreConversionErrors"]));
|
||||
}
|
||||
set {
|
||||
this["appIgnoreConversionErrors"] = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -80,5 +80,8 @@
|
||||
<Setting Name="appExtractAudio" Type="System.Boolean" Scope="User">
|
||||
<Value Profile="(Default)">True</Value>
|
||||
</Setting>
|
||||
<Setting Name="appIgnoreConversionErrors" Type="System.Boolean" Scope="User">
|
||||
<Value Profile="(Default)">False</Value>
|
||||
</Setting>
|
||||
</Settings>
|
||||
</SettingsFile>
|
||||
62
Strings/guiStrings.Designer.cs
generated
62
Strings/guiStrings.Designer.cs
generated
@@ -106,7 +106,7 @@ namespace GARbro.GUI.Strings {
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Convert images....
|
||||
/// Looks up a localized string similar to Convert multimedia....
|
||||
/// </summary>
|
||||
public static string CtxMenuConvert {
|
||||
get {
|
||||
@@ -304,7 +304,7 @@ namespace GARbro.GUI.Strings {
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Choose destination format.
|
||||
/// Looks up a localized string similar to Choose destination format for images.
|
||||
/// </summary>
|
||||
public static string LabelDestinationFormat {
|
||||
get {
|
||||
@@ -339,6 +339,15 @@ namespace GARbro.GUI.Strings {
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Skip incovertible files..
|
||||
/// </summary>
|
||||
public static string LabelSkipFailures {
|
||||
get {
|
||||
return ResourceManager.GetString("LabelSkipFailures", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to About Game Resource browser.
|
||||
/// </summary>
|
||||
@@ -457,11 +466,11 @@ namespace GARbro.GUI.Strings {
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Converting image {0}.
|
||||
/// Looks up a localized string similar to Converting file {0}.
|
||||
/// </summary>
|
||||
public static string MsgConvertingImage {
|
||||
public static string MsgConvertingFile {
|
||||
get {
|
||||
return ResourceManager.GetString("MsgConvertingImage", resourceCulture);
|
||||
return ResourceManager.GetString("MsgConvertingFile", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -627,6 +636,15 @@ namespace GARbro.GUI.Strings {
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to No media files selected..
|
||||
/// </summary>
|
||||
public static string MsgNoMediaFiles {
|
||||
get {
|
||||
return ResourceManager.GetString("MsgNoMediaFiles", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to File {0}
|
||||
///already exists.
|
||||
@@ -648,12 +666,21 @@ namespace GARbro.GUI.Strings {
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to unable to interpret audio format.
|
||||
/// </summary>
|
||||
public static string MsgUnableInterpretAudio {
|
||||
get {
|
||||
return ResourceManager.GetString("MsgUnableInterpretAudio", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to unable to interpret image format.
|
||||
/// </summary>
|
||||
public static string MsgUnableInterpret {
|
||||
public static string MsgUnableInterpretImage {
|
||||
get {
|
||||
return ResourceManager.GetString("MsgUnableInterpret", resourceCulture);
|
||||
return ResourceManager.GetString("MsgUnableInterpretImage", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -747,6 +774,15 @@ namespace GARbro.GUI.Strings {
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Audio will be converted to either WAV, MP3 or OGG..
|
||||
/// </summary>
|
||||
public static string TextAudioConversion {
|
||||
get {
|
||||
return ResourceManager.GetString("TextAudioConversion", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Choose archive location.
|
||||
/// </summary>
|
||||
@@ -784,11 +820,11 @@ namespace GARbro.GUI.Strings {
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Convert images.
|
||||
/// Looks up a localized string similar to Media conversion.
|
||||
/// </summary>
|
||||
public static string TextConvertImages {
|
||||
public static string TextConvertMedia {
|
||||
get {
|
||||
return ResourceManager.GetString("TextConvertImages", resourceCulture);
|
||||
return ResourceManager.GetString("TextConvertMedia", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -865,11 +901,11 @@ namespace GARbro.GUI.Strings {
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Image conversion error.
|
||||
/// Looks up a localized string similar to Multimedia conversion error.
|
||||
/// </summary>
|
||||
public static string TextImageConvertError {
|
||||
public static string TextMediaConvertError {
|
||||
get {
|
||||
return ResourceManager.GetString("TextImageConvertError", resourceCulture);
|
||||
return ResourceManager.GetString("TextMediaConvertError", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -240,7 +240,7 @@
|
||||
<data name="MsgReady" xml:space="preserve">
|
||||
<value>Ready</value>
|
||||
</data>
|
||||
<data name="MsgUnableInterpret" xml:space="preserve">
|
||||
<data name="MsgUnableInterpretImage" xml:space="preserve">
|
||||
<value>unable to interpret image format</value>
|
||||
</data>
|
||||
<data name="MsgUnknownFormat" xml:space="preserve">
|
||||
@@ -379,22 +379,22 @@ Overwrite?</value>
|
||||
<value>_View</value>
|
||||
</data>
|
||||
<data name="CtxMenuConvert" xml:space="preserve">
|
||||
<value>Convert images...</value>
|
||||
<value>Convert multimedia...</value>
|
||||
</data>
|
||||
<data name="ButtonConvert" xml:space="preserve">
|
||||
<value>Convert</value>
|
||||
</data>
|
||||
<data name="LabelDestinationFormat" xml:space="preserve">
|
||||
<value>Choose destination format</value>
|
||||
<value>Choose destination format for images</value>
|
||||
</data>
|
||||
<data name="TextConvertImages" xml:space="preserve">
|
||||
<value>Convert images</value>
|
||||
<data name="TextConvertMedia" xml:space="preserve">
|
||||
<value>Media conversion</value>
|
||||
</data>
|
||||
<data name="MsgConvertingImage" xml:space="preserve">
|
||||
<value>Converting image {0}</value>
|
||||
<data name="MsgConvertingFile" xml:space="preserve">
|
||||
<value>Converting file {0}</value>
|
||||
</data>
|
||||
<data name="TextImageConvertError" xml:space="preserve">
|
||||
<value>Image conversion error</value>
|
||||
<data name="TextMediaConvertError" xml:space="preserve">
|
||||
<value>Multimedia conversion error</value>
|
||||
</data>
|
||||
<data name="LabelEncoding" xml:space="preserve">
|
||||
<value>Encoding</value>
|
||||
@@ -414,4 +414,16 @@ Overwrite?</value>
|
||||
<data name="TextAboutLicense" xml:space="preserve">
|
||||
<value>License</value>
|
||||
</data>
|
||||
<data name="MsgUnableInterpretAudio" xml:space="preserve">
|
||||
<value>unable to interpret audio format</value>
|
||||
</data>
|
||||
<data name="MsgNoMediaFiles" xml:space="preserve">
|
||||
<value>No media files selected.</value>
|
||||
</data>
|
||||
<data name="TextAudioConversion" xml:space="preserve">
|
||||
<value>Audio will be converted to either WAV, MP3 or OGG.</value>
|
||||
</data>
|
||||
<data name="LabelSkipFailures" xml:space="preserve">
|
||||
<value>Skip incovertible files.</value>
|
||||
</data>
|
||||
</root>
|
||||
@@ -234,7 +234,7 @@
|
||||
<data name="MsgReady" xml:space="preserve">
|
||||
<value>Готов</value>
|
||||
</data>
|
||||
<data name="MsgUnableInterpret" xml:space="preserve">
|
||||
<data name="MsgUnableInterpretImage" xml:space="preserve">
|
||||
<value>не удалось интерпретировать формат изображения</value>
|
||||
</data>
|
||||
<data name="MsgUnknownFormat" xml:space="preserve">
|
||||
@@ -394,22 +394,22 @@
|
||||
<value>Просмотр</value>
|
||||
</data>
|
||||
<data name="CtxMenuConvert" xml:space="preserve">
|
||||
<value>Конверсия изображений...</value>
|
||||
<value>Конверсия мультимедиа...</value>
|
||||
</data>
|
||||
<data name="ButtonConvert" xml:space="preserve">
|
||||
<value>Преобразовать</value>
|
||||
</data>
|
||||
<data name="LabelDestinationFormat" xml:space="preserve">
|
||||
<value>Формат преобразования</value>
|
||||
<value>Формат преобразования изображений</value>
|
||||
</data>
|
||||
<data name="TextConvertImages" xml:space="preserve">
|
||||
<value>Преобразовать изображения</value>
|
||||
<data name="TextConvertMedia" xml:space="preserve">
|
||||
<value>Преобразование мультимедиа</value>
|
||||
</data>
|
||||
<data name="MsgConvertingImage" xml:space="preserve">
|
||||
<data name="MsgConvertingFile" xml:space="preserve">
|
||||
<value>Преобразование файла {0}</value>
|
||||
</data>
|
||||
<data name="TextImageConvertError" xml:space="preserve">
|
||||
<value>Ошибка конверсии изображения</value>
|
||||
<data name="TextMediaConvertError" xml:space="preserve">
|
||||
<value>Ошибка конверсии мультимедиа</value>
|
||||
</data>
|
||||
<data name="LabelEncoding" xml:space="preserve">
|
||||
<value>Кодировка</value>
|
||||
@@ -432,4 +432,16 @@
|
||||
<data name="Type_audio" xml:space="preserve">
|
||||
<value>звук</value>
|
||||
</data>
|
||||
<data name="MsgUnableInterpretAudio" xml:space="preserve">
|
||||
<value>не удалось интерпретировать формат аудио</value>
|
||||
</data>
|
||||
<data name="LabelSkipFailures" xml:space="preserve">
|
||||
<value>Пропускать файлы, не поддавшиеся конверсии.</value>
|
||||
</data>
|
||||
<data name="MsgNoMediaFiles" xml:space="preserve">
|
||||
<value>Среди выбранных файлов нет мультиемедиа.</value>
|
||||
</data>
|
||||
<data name="TextAudioConversion" xml:space="preserve">
|
||||
<value>Аудио-файлы будут преобразованы в WAV, MP3 или OGG.</value>
|
||||
</data>
|
||||
</root>
|
||||
11
ViewModel.cs
11
ViewModel.cs
@@ -39,17 +39,6 @@ using GARbro.GUI.Strings;
|
||||
|
||||
namespace GARbro.GUI
|
||||
{
|
||||
public class SubDirEntry : GameRes.Entry
|
||||
{
|
||||
public override string Type { get { return "directory"; } }
|
||||
|
||||
public SubDirEntry (string name)
|
||||
{
|
||||
Name = name;
|
||||
Size = 0;
|
||||
}
|
||||
}
|
||||
|
||||
public class DirectoryViewModel : ObservableCollection<EntryViewModel>
|
||||
{
|
||||
public string Path { get; private set; }
|
||||
|
||||
@@ -19,15 +19,18 @@ tr.odd td { background-color: #eee }
|
||||
<tr><td>*.bip</td><td>-</td><td>No</td></tr>
|
||||
<tr class="odd"><td>data.ami</td><td><tt>AMI</tt></td><td>Yes</td><td>-</td><td>Muv-Luv Amaterasu Translation data files</td></tr>
|
||||
<tr><td>*.arc</td><td><tt>PackFile</tt></td><td>No</td><td>BGI/Ethornell</td><td>Chou Dengeki Stryker</td></tr>
|
||||
<tr class="odd"><td>*</td><td>-<br/><tt>SM2MPX10</tt></td><td>No</td><td rowspan="3">DRS</td><td rowspan="3">
|
||||
<tr class="odd"><td>*</td><td>-<br/><tt>SM2MPX10</tt></td><td>No</td><td rowspan="4">DRS</td><td rowspan="4">
|
||||
Anata no Osanazuma<br/>
|
||||
Ecchi na Bunny-san wa Kirai?<br/>
|
||||
Kana ~Imouto~<br/>
|
||||
Natsu no Hitoshizuku<br/>
|
||||
Private Nurse<br/>
|
||||
Sensei 2<br/>
|
||||
Shoujo Settai<br/>
|
||||
</td></tr>
|
||||
<tr class="odd"><td>*.ggd</td><td><tt>\xB9\xAA\xB3\xB3</tt><br/><tt>\xAB\xAD\xAA\xBA</tt><br/><tt>\xB7\xB6\xB8\xB7</tt><br/><tt>\xCD\xCA\xC9\xB8</tt></td><td>Yes</td></tr>
|
||||
<tr class="odd"><td>*.gg1<br/>*.gg2<br/>*.gg3</td><td><tt>GGA00000</tt></td><td>No</td></tr>
|
||||
<tr class="odd"><td>*.gg1<br/>*.gg2<br/>*.gg3<br/>*.gg0</td><td><tt>GGA00000</tt></td><td>No</td></tr>
|
||||
<tr class="odd"><td>*.ggp</td><td><tt>GGPFAIKE</tt></td><td>No</td></tr>
|
||||
<tr><td>*.bin</td><td><tt>ACPXPK01</tt></td><td>No</td><td>Unison Shift</td><td>Wasurenagusa ~Forget-me-Not~</td></tr>
|
||||
<tr class="odd"><td>*.gsp</td><td>-</td><td>No</td><td rowspan="2">Black Rainbow</td><td rowspan="2">Saimin Gakuen</td></tr>
|
||||
<tr class="odd"><td>*.bmz</td><td><tt>ZLC3</tt></td><td>Yes</td></tr>
|
||||
@@ -136,17 +139,20 @@ Nikutai Ten'i<br/>
|
||||
</td></tr>
|
||||
<tr class="odd"><td>*.grd</td><td><tt>CMP_</tt></td><td>No</td></tr>
|
||||
<tr><td>*.dat<br/>*.arc</td><td>-<br/><tt>M2TYPE</tt></td><td>No</td><td rowspan="2">FFA System/G-SYS</td><td rowspan="2">
|
||||
Onsen Kankou Yukemuri Chijou<br/>
|
||||
Dokusen Kango<br/>
|
||||
Inran OL Sawatari Tokiko<br/>
|
||||
Onsen Kankou Yukemuri Chijou<br/>
|
||||
</td></tr>
|
||||
<tr><td>*.pt1</td><td>-</td><td>No</td></tr>
|
||||
<tr class="odd"><td>*.pak</td><td><tt>DataPack5</tt><br/><tt>GsPack4</tt></td><td>No</td><td rowspan="2">Root</td><td rowspan="2">Para-Sol</td></tr>
|
||||
<tr class="odd"><td>*</td><td><tt>\x00\x00\x04\x00</tt></td><td>No</td></tr>
|
||||
<tr><td>*.war</td><td><tt>WARC 1.7</tt></td><td>No</td><td rowspan="2">Shiina Rio</td><td rowspan="2">
|
||||
Helter Skelter<br/>
|
||||
<tr><td>*.war</td><td><tt>WARC 1.7</tt></td><td>No</td><td rowspan="3">Shiina Rio</td><td rowspan="3">
|
||||
Classmate no Okaa-san<br/>
|
||||
Helter Skelter<br/>
|
||||
Mahou Shoujo no Taisetsu na Koto<br/>
|
||||
</td></tr>
|
||||
<tr><td>*.s25</td><td><tt>S25</tt></td><td>No</td></tr>
|
||||
<tr><td>*.ogv</td><td><tt>OGV</tt></td><td>No</td></tr>
|
||||
<tr class="odd"><td>*</td><td><tt>ARC2</tt><br/><tt>ARC1</tt></td><td>No</td><td>AST</td><td>Jokyoushi wo Kurau</td></tr>
|
||||
<tr><td>*.dat</td><td>-</td><td>No</td><td>Ail</td><td>Ragna☆彡Science</td></tr>
|
||||
<tr class="odd"><td>*.lpk</td><td><tt>LPK1</tt></td><td>No</td><td rowspan="2">Lucifen</td><td rowspan="2">
|
||||
@@ -185,8 +191,20 @@ Yatohime Zankikou<br/>
|
||||
Onna Kyoushi
|
||||
</td></tr>
|
||||
<tr class="odd"><td>*.bg_<br/>*.cg_</td><td><tt>AP</tt></td><td>Yes</td></tr>
|
||||
<tr><td>*.dpk</td><td>DPK</td><td>No</td><td rowspan="2">DAC</td><td rowspan="2">Yumemiru Tsuki no Lunalutia</td></tr>
|
||||
<tr><td>*.dgc</td><td>DGC</td><td>No</td></tr>
|
||||
<tr><td>*.dpk</td><td><tt>DPK</tt></td><td>No</td><td rowspan="2">DAC</td><td rowspan="2">Yumemiru Tsuki no Lunalutia</td></tr>
|
||||
<tr><td>*.dgc</td><td><tt>DGC</tt></td><td>No</td></tr>
|
||||
<tr class="odd"><td>*.pck</td><td>-</td><td>No</td><td rowspan="5">Crowd</td><td rowspan="5">
|
||||
X Change R<br/>
|
||||
X Change<br/>
|
||||
X Change 2<br/>
|
||||
</td></tr>
|
||||
<tr class="odd"><td>*.cwp</td><td><tt>CWDP</tt></td><td>Yes</td></tr>
|
||||
<tr class="odd"><td>*.cwd</td><td><tt>cwd</tt></td><td>No</td></tr>
|
||||
<tr class="odd"><td>*.eog</td><td><tt>CRM</tt></td><td>No</td></tr>
|
||||
<tr class="odd"><td>*.zbm<br/>*.cwl</td><td><tt>SZDD</tt></td><td>No</td></tr>
|
||||
<tr><td>*.pack</td><td><tt>FilePackVer2.0</tt></td><td>No</td><td>QLIE</td><td>
|
||||
Mehime no Toriko<br/>
|
||||
</td></tr>
|
||||
</table>
|
||||
<p><a name="note-1">[1]</a> Non-encrypted only</p>
|
||||
</body>
|
||||
|
||||
Reference in New Issue
Block a user