mirror of
https://github.com/crskycode/GARbro.git
synced 2024-12-24 03:44:13 +08:00
implemented IAR and WAR archives.
This commit is contained in:
parent
86d9121b94
commit
f901f8252f
@ -70,6 +70,10 @@
|
||||
<Compile Include="Debonosu\ArcPAK.cs" />
|
||||
<Compile Include="elf\ImageG24.cs" />
|
||||
<Compile Include="elf\ImageGP8.cs" />
|
||||
<Compile Include="Sas5\ArcIAR.cs" />
|
||||
<Compile Include="Sas5\ArcSec5.cs" />
|
||||
<Compile Include="Sas5\ArcWAR.cs" />
|
||||
<Compile Include="Sas5\ImageIAR.cs" />
|
||||
<Compile Include="Silky\ImageMSK.cs" />
|
||||
<Compile Include="Emic\ArcPACK.cs" />
|
||||
<Compile Include="Emic\ImageMWP.cs" />
|
||||
|
364
ArcFormats/Sas5/ArcIAR.cs
Normal file
364
ArcFormats/Sas5/ArcIAR.cs
Normal file
@ -0,0 +1,364 @@
|
||||
//! \file ArcIAR.cs
|
||||
//! \date Fri Oct 23 12:31:15 2015
|
||||
//! \brief Sas5 engine image 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;
|
||||
using System.Text;
|
||||
|
||||
namespace GameRes.Formats.Sas5
|
||||
{
|
||||
internal class IarArchive : ArcFile
|
||||
{
|
||||
public readonly int Version;
|
||||
|
||||
public IarArchive (ArcView arc, ArchiveFormat impl, ICollection<Entry> dir, int version)
|
||||
: base (arc, impl, dir)
|
||||
{
|
||||
Version = version;
|
||||
}
|
||||
}
|
||||
|
||||
internal class IarImageInfo : ImageMetaData
|
||||
{
|
||||
public int Flags;
|
||||
public bool Compressed;
|
||||
public uint PaletteSize;
|
||||
public int PackedSize;
|
||||
public int UnpackedSize;
|
||||
public int Stride;
|
||||
}
|
||||
|
||||
[Export(typeof(ArchiveFormat))]
|
||||
public class IarOpener : ArchiveFormat
|
||||
{
|
||||
public override string Tag { get { return "IAR"; } }
|
||||
public override string Description { get { return "SAS5 engine images archive"; } }
|
||||
public override uint Signature { get { return 0x20726169; } } // 'iar '
|
||||
public override bool IsHierarchic { get { return false; } }
|
||||
public override bool CanCreate { get { return false; } }
|
||||
|
||||
public override ArcFile TryOpen (ArcView file)
|
||||
{
|
||||
int version = file.View.ReadInt16 (4);
|
||||
if (version < 1 || version > 4)
|
||||
return null;
|
||||
int file_count = file.View.ReadInt32 (0x18);
|
||||
int count = file.View.ReadInt32 (0x1C);
|
||||
if (count < file_count || !IsSaneCount (count))
|
||||
return null;
|
||||
|
||||
var index = Sec5Opener.LookupIndex (file.Name);
|
||||
string base_name = Path.GetFileNameWithoutExtension (file.Name);
|
||||
Func<int, Entry> CreateEntry;
|
||||
if (null == index)
|
||||
CreateEntry = n => GetDefaultEntry (base_name, n);
|
||||
else
|
||||
CreateEntry = (n) => {
|
||||
Entry entry;
|
||||
if (index.TryGetValue (n, out entry))
|
||||
return new Entry { Name = entry.Name, Type = entry.Type };
|
||||
return GetDefaultEntry (base_name, n);
|
||||
};
|
||||
|
||||
uint offset_size = version < 3 ? 4u : 8u;
|
||||
Func<uint, long> ReadOffset;
|
||||
if (version < 3)
|
||||
ReadOffset = x => file.View.ReadUInt32 (x);
|
||||
else
|
||||
ReadOffset = x => file.View.ReadInt64 (x);
|
||||
|
||||
uint index_offset = 0x20;
|
||||
var dir = new List<Entry> (count);
|
||||
var next_offset = ReadOffset (index_offset);
|
||||
for (int i = 0; i < count; ++i)
|
||||
{
|
||||
var entry = CreateEntry (i);
|
||||
entry.Offset = next_offset;
|
||||
index_offset += offset_size;
|
||||
next_offset = (i + 1) == count ? file.MaxOffset : ReadOffset (index_offset);
|
||||
entry.Size = (uint)(next_offset - entry.Offset);
|
||||
if (!entry.CheckPlacement (file.MaxOffset))
|
||||
return null;
|
||||
dir.Add (entry);
|
||||
}
|
||||
return new IarArchive (file, this, dir, version);
|
||||
}
|
||||
|
||||
static Entry GetDefaultEntry (string base_name, int n)
|
||||
{
|
||||
return new Entry { Name = string.Format ("{0}#{1:D5}", base_name, n) };
|
||||
}
|
||||
|
||||
public override Stream OpenEntry (ArcFile arc, Entry entry)
|
||||
{
|
||||
var iarc = arc as IarArchive;
|
||||
if (null == iarc)
|
||||
return base.OpenEntry (arc, entry);
|
||||
int flags = arc.File.View.ReadUInt16 (entry.Offset);
|
||||
if (0 != (flags & 0x1000))
|
||||
return base.OpenEntry (arc, entry);
|
||||
|
||||
using (var image = new IarImage (iarc, entry))
|
||||
{
|
||||
byte[] pixels = image.Data;
|
||||
if (0 != (flags & 0x800))
|
||||
pixels = CombineImage (pixels, image.Info, iarc);
|
||||
|
||||
// internal 'IAR SAS5' format
|
||||
var header = new byte[0x28+image.Info.PaletteSize];
|
||||
using (var mem = new MemoryStream (header))
|
||||
using (var writer = new BinaryWriter (mem))
|
||||
{
|
||||
writer.Write (0x00524149); // 'IAR'
|
||||
writer.Write (0x35534153); // 'SAS5'
|
||||
writer.Write (image.Info.Width);
|
||||
writer.Write (image.Info.Height);
|
||||
writer.Write (image.Info.OffsetX);
|
||||
writer.Write (image.Info.OffsetY);
|
||||
writer.Write (image.Info.BPP);
|
||||
writer.Write (image.Info.Stride);
|
||||
writer.Write (image.Info.PaletteSize);
|
||||
writer.Write (pixels.Length);
|
||||
if (null != image.Palette)
|
||||
writer.Write (image.Palette, 0, image.Palette.Length);
|
||||
return new PrefixStream (header, new MemoryStream (pixels));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
byte[] CombineImage (byte[] overlay, IarImageInfo info, IarArchive iarc)
|
||||
{
|
||||
using (var mem = new MemoryStream (overlay))
|
||||
using (var input = new BinaryReader (mem))
|
||||
{
|
||||
var dir = (List<Entry>)iarc.Dir;
|
||||
int base_index = input.ReadInt32();
|
||||
if (base_index >= dir.Count)
|
||||
throw new InvalidFormatException ("Invalid base image index");
|
||||
int diff_y = input.ReadInt32();
|
||||
int diff_count = input.ReadInt32();
|
||||
using (var base_image = new IarImage (iarc, dir[base_index]))
|
||||
{
|
||||
int base_y = (int)base_image.Info.Height - (int)info.Height;
|
||||
byte[] output = base_image.Data;
|
||||
if (base_y != 0 || info.Stride != base_image.Info.Stride)
|
||||
{
|
||||
byte[] src = base_image.Data;
|
||||
int base_stride = Math.Min (info.Stride, base_image.Info.Stride);
|
||||
output = new byte[info.Height * info.Stride];
|
||||
for (int y = base_y; y < base_image.Info.Height; ++y)
|
||||
{
|
||||
Buffer.BlockCopy (src, y * base_image.Info.Stride,
|
||||
output, (y - base_y) * info.Stride, base_stride);
|
||||
}
|
||||
}
|
||||
int pixel_size = info.BPP / 8;
|
||||
int dst = diff_y * info.Stride;
|
||||
for (int i = 0; i < diff_count; ++i)
|
||||
{
|
||||
int chunk_count = input.ReadUInt16();
|
||||
int x = 0;
|
||||
for (int j = 0; j < chunk_count; ++j)
|
||||
{
|
||||
int skip_count = pixel_size * input.ReadUInt16();
|
||||
int copy_count = pixel_size * input.ReadUInt16();
|
||||
|
||||
x += skip_count;
|
||||
input.Read (output, dst+x, copy_count);
|
||||
x += copy_count;
|
||||
}
|
||||
dst += info.Stride;
|
||||
}
|
||||
return output;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal sealed class IarImage : IDisposable
|
||||
{
|
||||
BinaryReader m_input;
|
||||
IarImageInfo m_info;
|
||||
byte[] m_palette;
|
||||
byte[] m_output;
|
||||
|
||||
public IarImageInfo Info { get { return m_info; } }
|
||||
public byte[] Palette { get { return m_palette; } }
|
||||
public byte[] Data { get { return m_output; } }
|
||||
|
||||
public IarImage (IarArchive iarc, Entry entry)
|
||||
{
|
||||
int flags = iarc.File.View.ReadUInt16 (entry.Offset);
|
||||
int bpp;
|
||||
switch (flags & 0x3E)
|
||||
{
|
||||
case 0x02: bpp = 8; break;
|
||||
case 0x1C: bpp = 24; break;
|
||||
case 0x3C: bpp = 32; break;
|
||||
default: throw new NotSupportedException ("Not supported IAR image format");
|
||||
}
|
||||
var offset = entry.Offset;
|
||||
m_info = new IarImageInfo
|
||||
{
|
||||
Flags = flags,
|
||||
BPP = bpp,
|
||||
Compressed = iarc.File.View.ReadByte (offset+3) != 0,
|
||||
Width = iarc.File.View.ReadUInt32 (offset+0x20),
|
||||
Height = iarc.File.View.ReadUInt32 (offset+0x24),
|
||||
Stride = iarc.File.View.ReadInt32 (offset+0x28),
|
||||
OffsetX = iarc.File.View.ReadInt32 (offset+0x18),
|
||||
OffsetY = iarc.File.View.ReadInt32 (offset+0x1C),
|
||||
UnpackedSize = iarc.File.View.ReadInt32 (offset+8),
|
||||
PaletteSize = iarc.File.View.ReadUInt32 (offset+0xC),
|
||||
PackedSize = iarc.File.View.ReadInt32 (offset+0x10),
|
||||
};
|
||||
uint header_size = 1 == iarc.Version ? 0x30u : iarc.Version < 4 ? 0x40u : 0x48u;
|
||||
offset += header_size;
|
||||
uint input_size = entry.Size - header_size;
|
||||
|
||||
if (m_info.PaletteSize > 0)
|
||||
{
|
||||
m_palette = new byte[m_info.PaletteSize];
|
||||
iarc.File.View.Read (offset, m_palette, 0, m_info.PaletteSize);
|
||||
offset += m_info.PaletteSize;
|
||||
input_size -= m_info.PaletteSize;
|
||||
}
|
||||
var input = iarc.File.CreateStream (offset, input_size);
|
||||
m_input = new BinaryReader (input);
|
||||
m_output = new byte[m_info.UnpackedSize];
|
||||
if (!m_info.Compressed)
|
||||
m_input.Read (m_output, 0, m_output.Length);
|
||||
else
|
||||
Unpack();
|
||||
}
|
||||
|
||||
void Unpack ()
|
||||
{
|
||||
m_bits = 1;
|
||||
int dst = 0;
|
||||
while (dst < m_output.Length)
|
||||
{
|
||||
if (1 == GetNextBit())
|
||||
{
|
||||
m_output[dst++] = m_input.ReadByte();
|
||||
continue;
|
||||
}
|
||||
int offset, count;
|
||||
if (1 == GetNextBit())
|
||||
{
|
||||
int tmp = GetNextBit();
|
||||
if (1 == GetNextBit())
|
||||
offset = 1;
|
||||
else if (1 == GetNextBit())
|
||||
offset = 0x201;
|
||||
else
|
||||
{
|
||||
tmp = (tmp << 1) | GetNextBit();
|
||||
if (1 == GetNextBit())
|
||||
offset = 0x401;
|
||||
else
|
||||
{
|
||||
tmp = (tmp << 1) | GetNextBit();
|
||||
if (1 == GetNextBit())
|
||||
offset = 0x801;
|
||||
else
|
||||
{
|
||||
offset = 0x1001;
|
||||
tmp = (tmp << 1) | GetNextBit();
|
||||
}
|
||||
}
|
||||
}
|
||||
offset += (tmp << 8) | m_input.ReadByte();
|
||||
if (1 == GetNextBit())
|
||||
count = 3;
|
||||
else if (1 == GetNextBit())
|
||||
count = 4;
|
||||
else if (1 == GetNextBit())
|
||||
count = 5;
|
||||
else if (1 == GetNextBit())
|
||||
count = 6;
|
||||
else if (1 == GetNextBit())
|
||||
count = 7 + GetNextBit();
|
||||
else if (1 == GetNextBit())
|
||||
count = 17 + m_input.ReadByte();
|
||||
else
|
||||
{
|
||||
count = GetNextBit() << 2;
|
||||
count |= GetNextBit() << 1;
|
||||
count |= GetNextBit();
|
||||
count += 9;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
count = 2;
|
||||
if (1 == GetNextBit())
|
||||
{
|
||||
offset = GetNextBit() << 10;
|
||||
offset |= GetNextBit() << 9;
|
||||
offset |= GetNextBit() << 8;
|
||||
offset = (offset | m_input.ReadByte()) + 0x100;
|
||||
}
|
||||
else
|
||||
{
|
||||
offset = 1 + m_input.ReadByte();
|
||||
if (0x100 == offset)
|
||||
break;
|
||||
}
|
||||
}
|
||||
Binary.CopyOverlapped (m_output, dst - offset, dst, count);
|
||||
dst += count;
|
||||
}
|
||||
}
|
||||
|
||||
int m_bits = 1;
|
||||
|
||||
int GetNextBit ()
|
||||
{
|
||||
if (1 == m_bits)
|
||||
{
|
||||
m_bits = m_input.ReadUInt16() | 0x10000;
|
||||
}
|
||||
int b = m_bits & 1;
|
||||
m_bits >>= 1;
|
||||
return b;
|
||||
}
|
||||
|
||||
#region IDisposable Members
|
||||
bool _disposed = false;
|
||||
public void Dispose ()
|
||||
{
|
||||
if (!_disposed)
|
||||
{
|
||||
m_input.Dispose();
|
||||
_disposed = true;
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
173
ArcFormats/Sas5/ArcSec5.cs
Normal file
173
ArcFormats/Sas5/ArcSec5.cs
Normal file
@ -0,0 +1,173 @@
|
||||
//! \file ArcSec5.cs
|
||||
//! \date Fri Oct 23 18:10:06 2015
|
||||
//! \brief Sas5 engine resource index 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.Collections.Generic;
|
||||
using System.ComponentModel.Composition;
|
||||
using System.IO;
|
||||
using GameRes.Utility;
|
||||
using System.Text;
|
||||
|
||||
namespace GameRes.Formats.Sas5
|
||||
{
|
||||
[Export(typeof(ArchiveFormat))]
|
||||
public class Sec5Opener : ArchiveFormat
|
||||
{
|
||||
public override string Tag { get { return "SEC5"; } }
|
||||
public override string Description { get { return "SAS5 engine resource index file"; } }
|
||||
public override uint Signature { get { return 0x35434553; } } // 'SEC5'
|
||||
public override bool IsHierarchic { get { return false; } }
|
||||
public override bool CanCreate { get { return false; } }
|
||||
|
||||
public override ArcFile TryOpen (ArcView file)
|
||||
{
|
||||
uint offset = 8;
|
||||
var dir = new List<Entry>();
|
||||
while (offset < file.MaxOffset)
|
||||
{
|
||||
string name = file.View.ReadString (offset, 4, Encoding.ASCII);
|
||||
if ("ENDS" == name)
|
||||
break;
|
||||
uint section_size = file.View.ReadUInt32 (offset+4);
|
||||
offset += 8;
|
||||
var entry = new Entry {
|
||||
Name = name,
|
||||
Offset = offset,
|
||||
Size = section_size,
|
||||
};
|
||||
dir.Add (entry);
|
||||
offset += section_size;
|
||||
}
|
||||
if (dir.Count > 0)
|
||||
return new ArcFile (file, this, dir);
|
||||
return null;
|
||||
}
|
||||
|
||||
public override Stream OpenEntry (ArcFile arc, Entry entry)
|
||||
{
|
||||
if ("CODE" != entry.Name)
|
||||
return arc.File.CreateStream (entry.Offset, entry.Size);
|
||||
|
||||
var code = new byte[entry.Size];
|
||||
arc.File.View.Read (entry.Offset, code, 0, entry.Size);
|
||||
DecryptCodeSection (code);
|
||||
return new MemoryStream (code);
|
||||
}
|
||||
|
||||
static void DecryptCodeSection (byte[] code)
|
||||
{
|
||||
byte key = 0;
|
||||
for (int i = 0; i < code.Length; ++i)
|
||||
{
|
||||
int x = code[i] + 18;
|
||||
code[i] ^= key;
|
||||
key += (byte)x;
|
||||
}
|
||||
}
|
||||
|
||||
static internal Dictionary<string, Dictionary<int, Entry>> CurrentIndex;
|
||||
|
||||
static internal Dictionary<int, Entry> LookupIndex (string filename)
|
||||
{
|
||||
if (null == CurrentIndex)
|
||||
CurrentIndex = FindSec5Resr (filename);
|
||||
if (null == CurrentIndex)
|
||||
return null;
|
||||
Dictionary<int, Entry> arc_map = null;
|
||||
CurrentIndex.TryGetValue (Path.GetFileName (filename), out arc_map);
|
||||
return arc_map;
|
||||
}
|
||||
|
||||
static internal Dictionary<string, Dictionary<int, Entry>> FindSec5Resr (string arc_name)
|
||||
{
|
||||
string dir_name = Path.GetDirectoryName (arc_name);
|
||||
var match = Directory.GetFiles (dir_name, "*.sec5");
|
||||
if (0 == match.Length)
|
||||
{
|
||||
string parent = Path.GetDirectoryName (dir_name);
|
||||
if (!string.IsNullOrEmpty (parent))
|
||||
match = Directory.GetFiles (parent, "*.sec5");
|
||||
}
|
||||
if (0 == match.Length)
|
||||
return null;
|
||||
using (var sec5 = new ArcView (match[0]))
|
||||
{
|
||||
if (!sec5.View.AsciiEqual (0, "SEC5"))
|
||||
return null;
|
||||
uint offset = 8;
|
||||
while (offset < sec5.MaxOffset)
|
||||
{
|
||||
string id = sec5.View.ReadString (offset, 4, Encoding.ASCII);
|
||||
if ("ENDS" == id)
|
||||
break;
|
||||
uint section_size = sec5.View.ReadUInt32 (offset+4);
|
||||
offset += 8;
|
||||
if ("RESR" == id)
|
||||
{
|
||||
using (var resr = sec5.CreateStream (offset, section_size))
|
||||
return ReadResrSection (resr);
|
||||
}
|
||||
offset += section_size;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
static internal Dictionary<string, Dictionary<int, Entry>> ReadResrSection (Stream input)
|
||||
{
|
||||
using (var resr = new BinaryReader (input, Encodings.cp932, true))
|
||||
{
|
||||
int count = resr.ReadInt32();
|
||||
if (0 == count)
|
||||
return null;
|
||||
var map = new Dictionary<string, Dictionary<int, Entry>> (StringComparer.InvariantCultureIgnoreCase);
|
||||
for (int i = 0; i < count; ++i)
|
||||
{
|
||||
string name = resr.BaseStream.ReadCString();
|
||||
string type = resr.BaseStream.ReadCString();
|
||||
string arc_type = resr.BaseStream.ReadCString();
|
||||
int res_length = resr.ReadInt32();
|
||||
var next_pos = resr.BaseStream.Position + res_length;
|
||||
if (arc_type == "file-war" || arc_type == "file-iar")
|
||||
{
|
||||
string arc_name = resr.BaseStream.ReadCString();
|
||||
int id = resr.ReadInt32();
|
||||
var base_arc_name = Path.GetFileName (arc_name);
|
||||
if (!map.ContainsKey (base_arc_name))
|
||||
map[base_arc_name] = new Dictionary<int, Entry>();
|
||||
var entry = new Entry
|
||||
{
|
||||
Name = name,
|
||||
Type = type,
|
||||
};
|
||||
map[base_arc_name][id] = entry;
|
||||
}
|
||||
resr.BaseStream.Position = next_pos;
|
||||
}
|
||||
return map.Count > 0 ? map : null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
135
ArcFormats/Sas5/ArcWAR.cs
Normal file
135
ArcFormats/Sas5/ArcWAR.cs
Normal file
@ -0,0 +1,135 @@
|
||||
//! \file ArcWAR.cs
|
||||
//! \date Fri Oct 23 19:11:56 2015
|
||||
//! \brief Sas5 engine audio 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.Sas5
|
||||
{
|
||||
internal class WarEntry : Entry
|
||||
{
|
||||
public int Format;
|
||||
}
|
||||
|
||||
[Export(typeof(ArchiveFormat))]
|
||||
public class WarOpener : ArchiveFormat
|
||||
{
|
||||
public override string Tag { get { return "WAR/SAS5"; } }
|
||||
public override string Description { get { return "SAS5 engine audio archive"; } }
|
||||
public override uint Signature { get { return 0x20726177; } } // 'war '
|
||||
public override bool IsHierarchic { get { return false; } }
|
||||
public override bool CanCreate { get { return false; } }
|
||||
|
||||
public WarOpener ()
|
||||
{
|
||||
Extensions = new string[] { "war" };
|
||||
}
|
||||
|
||||
public override ArcFile TryOpen (ArcView file)
|
||||
{
|
||||
int count = file.View.ReadInt32 (8);
|
||||
if (!IsSaneCount (count))
|
||||
return null;
|
||||
uint entry_size = file.View.ReadUInt32 (12);
|
||||
if (entry_size < 0x18)
|
||||
return null;
|
||||
|
||||
var index = Sec5Opener.LookupIndex (file.Name);
|
||||
string base_name = Path.GetFileNameWithoutExtension (file.Name);
|
||||
Func<int, string> GetEntryName;
|
||||
if (null == index)
|
||||
GetEntryName = n => GetDefaultName (base_name, n);
|
||||
else
|
||||
GetEntryName = (n) => {
|
||||
Entry entry;
|
||||
if (index.TryGetValue (n, out entry))
|
||||
return entry.Name;
|
||||
return GetDefaultName (base_name, n);
|
||||
};
|
||||
|
||||
uint index_offset = 0x10;
|
||||
var dir = new List<Entry> (count);
|
||||
for (int i = 0; i < count; ++i)
|
||||
{
|
||||
var entry = new WarEntry {
|
||||
Name = GetEntryName (i),
|
||||
Offset = file.View.ReadUInt32 (index_offset),
|
||||
Size = file.View.ReadUInt32 (index_offset+4),
|
||||
Format = file.View.ReadByte (index_offset+0x14),
|
||||
};
|
||||
if (!entry.CheckPlacement (file.MaxOffset))
|
||||
return null;
|
||||
if (0 == entry.Format)
|
||||
{
|
||||
entry.Name = Path.ChangeExtension (entry.Name, "wav");
|
||||
entry.Type = "audio";
|
||||
}
|
||||
else if (2 == entry.Format)
|
||||
{
|
||||
entry.Name = Path.ChangeExtension (entry.Name, "ogg");
|
||||
entry.Type = "audio";
|
||||
}
|
||||
dir.Add (entry);
|
||||
index_offset += entry_size;
|
||||
}
|
||||
return new ArcFile (file, this, dir);
|
||||
}
|
||||
|
||||
public override Stream OpenEntry (ArcFile arc, Entry entry)
|
||||
{
|
||||
var went = entry as WarEntry;
|
||||
if (null == went || 0 != went.Format)
|
||||
return arc.File.CreateStream (entry.Offset, entry.Size);
|
||||
|
||||
uint fmt_size = arc.File.View.ReadUInt32 (entry.Offset);
|
||||
uint data_size = arc.File.View.ReadUInt32 (entry.Offset+4);
|
||||
var wav_header = new byte[8+12+fmt_size+8];
|
||||
uint total_size = (uint)wav_header.Length + data_size - 8;
|
||||
arc.File.View.Read (entry.Offset+8, wav_header, 0x14, fmt_size);
|
||||
using (var mem = new MemoryStream (wav_header))
|
||||
using (var buffer = new BinaryWriter (mem))
|
||||
{
|
||||
buffer.Write (AudioFormat.Wav.Signature);
|
||||
buffer.Write (total_size);
|
||||
buffer.Write (0x45564157); // 'WAVE'
|
||||
buffer.Write (0x20746d66); // 'fmt '
|
||||
buffer.Write (fmt_size);
|
||||
buffer.BaseStream.Seek (fmt_size, SeekOrigin.Current);
|
||||
buffer.Write (0x61746164); // 'data'
|
||||
buffer.Write (data_size);
|
||||
}
|
||||
var pcm_data = arc.File.CreateStream (entry.Offset+8+fmt_size, entry.Size-8-fmt_size);
|
||||
return new PrefixStream (wav_header, pcm_data);
|
||||
}
|
||||
|
||||
static string GetDefaultName (string base_name, int n)
|
||||
{
|
||||
return string.Format ("{0}#{1:D5}", base_name, n);
|
||||
}
|
||||
}
|
||||
}
|
134
ArcFormats/Sas5/ImageIAR.cs
Normal file
134
ArcFormats/Sas5/ImageIAR.cs
Normal file
@ -0,0 +1,134 @@
|
||||
//! \file ImageIAR.cs
|
||||
//! \date Fri Oct 23 15:17:52 2015
|
||||
//! \brief Intermediate image format representing IAR archives entries.
|
||||
//
|
||||
// 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 GameRes.Utility;
|
||||
using System;
|
||||
using System.ComponentModel.Composition;
|
||||
using System.IO;
|
||||
using System.Windows.Media;
|
||||
using System.Windows.Media.Imaging;
|
||||
|
||||
namespace GameRes.Formats.Sas5
|
||||
{
|
||||
internal class IarMetaData : ImageMetaData
|
||||
{
|
||||
public int Stride;
|
||||
public int PaletteSize;
|
||||
public int ImageSize;
|
||||
}
|
||||
|
||||
// This is an artificial format, used by GARbro internally. structure:
|
||||
// 0x0000 'IAR\x00'
|
||||
// 0x0004 'SAS5'
|
||||
// 0x0008 Width
|
||||
// 0x000C Height
|
||||
// 0x0010 OffsetX
|
||||
// 0x0014 OffsetY
|
||||
// 0x0018 BPP
|
||||
// 0x001C Stride
|
||||
// 0x0020 PaletteSize
|
||||
// 0x0024 ImageSize
|
||||
// 0x0028 Palette [if PaletteSize > 0]
|
||||
// ...... Image data
|
||||
|
||||
[Export(typeof(ImageFormat))]
|
||||
public class IarFormat : ImageFormat
|
||||
{
|
||||
public override string Tag { get { return "IAR/IMAGE"; } }
|
||||
public override string Description { get { return "SAS5 engine compressed image format"; } }
|
||||
public override uint Signature { get { return 0x00524149; } } // 'IAR'
|
||||
|
||||
public IarFormat ()
|
||||
{
|
||||
Extensions = new string[] { "" };
|
||||
}
|
||||
|
||||
public override ImageMetaData ReadMetaData (Stream stream)
|
||||
{
|
||||
var header = new byte[0x28];
|
||||
if (header.Length != stream.Read (header, 0, header.Length))
|
||||
return null;
|
||||
if (!Binary.AsciiEqual (header, 4, "SAS5"))
|
||||
return null;
|
||||
return new IarMetaData
|
||||
{
|
||||
Width = LittleEndian.ToUInt32 (header, 0x08),
|
||||
Height = LittleEndian.ToUInt32 (header, 0x0C),
|
||||
OffsetX = LittleEndian.ToInt32 (header, 0x10),
|
||||
OffsetY = LittleEndian.ToInt32 (header, 0x14),
|
||||
BPP = LittleEndian.ToInt32 (header, 0x18),
|
||||
Stride = LittleEndian.ToInt32 (header, 0x1C),
|
||||
PaletteSize = LittleEndian.ToInt32 (header, 0x20),
|
||||
ImageSize = LittleEndian.ToInt32 (header, 0x24),
|
||||
};
|
||||
}
|
||||
|
||||
public override ImageData Read (Stream stream, ImageMetaData info)
|
||||
{
|
||||
var meta = info as IarMetaData;
|
||||
if (null == meta)
|
||||
throw new ArgumentException ("IarFormat.Read should be supplied with IarMetaData", "info");
|
||||
|
||||
PixelFormat format;
|
||||
if (32 == meta.BPP)
|
||||
format = PixelFormats.Bgra32;
|
||||
else if (24 == meta.BPP)
|
||||
format = PixelFormats.Bgr24;
|
||||
else if (0 == meta.PaletteSize)
|
||||
format = PixelFormats.Gray8;
|
||||
else
|
||||
format = PixelFormats.Indexed8;
|
||||
|
||||
stream.Position = 0x28;
|
||||
BitmapPalette palette = null;
|
||||
if (meta.PaletteSize > 0)
|
||||
palette = ReadPalette (stream, meta.PaletteSize);
|
||||
var pixels = new byte[meta.ImageSize];
|
||||
stream.Read (pixels, 0, pixels.Length);
|
||||
return ImageData.Create (info, format, palette, pixels, meta.Stride);
|
||||
}
|
||||
|
||||
public override void Write (Stream file, ImageData image)
|
||||
{
|
||||
throw new System.NotImplementedException ("IarFormat.Write not implemented");
|
||||
}
|
||||
|
||||
static BitmapPalette ReadPalette (Stream input, int palette_size)
|
||||
{
|
||||
var palette_data = new byte[palette_size];
|
||||
if (palette_data.Length != input.Read (palette_data, 0, palette_data.Length))
|
||||
throw new EndOfStreamException();
|
||||
palette_size = Math.Min (0x400, palette_size);
|
||||
int color_size = palette_size / 0x100;
|
||||
var palette = new Color[0x100];
|
||||
for (int i = 0; i < palette.Length; ++i)
|
||||
{
|
||||
int c = i * color_size;
|
||||
palette[i] = Color.FromRgb (palette_data[c+2], palette_data[c+1], palette_data[c]);
|
||||
}
|
||||
return new BitmapPalette (palette);
|
||||
}
|
||||
}
|
||||
}
|
@ -426,6 +426,15 @@ Blood Royal<br/>
|
||||
Heroine<br/>
|
||||
Promise<br/>
|
||||
</td></tr>
|
||||
<tr class="odd"><td>*.arc</td><td>-</td><td>No</td><td rowspan="3">Silky's</td><td rowspan="3">
|
||||
Shitai o Arau<br/>
|
||||
</td></tr>
|
||||
<tr class="odd"><td>*.g24</td><td>-</td><td>No</td></tr>
|
||||
<tr class="odd"><td>*.msk</td><td><tt>Rmsk</tt></td><td>No</td></tr>
|
||||
<tr><td>*.iar<br/>*.war</td><td><tt>iar</tt><br/><tt>war</tt></td><td>No</td><td>Studio Ryokucha</td><td>
|
||||
Katakoi no Tsuki<br/>
|
||||
Katakoi no Tsuki Extra<br/>
|
||||
</td></tr>
|
||||
</table>
|
||||
<p><a name="note-1" class="footnote">1</a> Non-encrypted only</p>
|
||||
</body>
|
||||
|
Loading…
x
Reference in New Issue
Block a user