mirror of
https://github.com/crskycode/GARbro.git
synced 2024-11-23 05:35:34 +08:00
Merge remote-tracking branch 'upstream/master'
This commit is contained in:
commit
ae9eb98d03
@ -235,8 +235,15 @@ namespace GameRes.Formats.Ankh
|
||||
|
||||
Stream OpenTpw (ArcFile arc, PackedEntry entry)
|
||||
{
|
||||
var output = new byte[entry.UnpackedSize];
|
||||
using (var input = arc.File.CreateStream (entry.Offset, entry.Size))
|
||||
{
|
||||
var output = new byte[entry.UnpackedSize];
|
||||
UnpackTpw (input, output);
|
||||
return new BinMemoryStream (output, entry.Name);
|
||||
}
|
||||
}
|
||||
|
||||
internal static void UnpackTpw (IBinaryStream input, byte[] output)
|
||||
{
|
||||
input.Position = 8;
|
||||
var offsets = new int[4];
|
||||
@ -250,10 +257,11 @@ namespace GameRes.Formats.Ankh
|
||||
byte ctl = input.ReadUInt8();
|
||||
if (0 == ctl)
|
||||
break;
|
||||
int remaining = output.Length - dst;
|
||||
int count;
|
||||
if (ctl < 0x40)
|
||||
{
|
||||
count = Math.Min (ctl, output.Length - dst);
|
||||
count = Math.Min (ctl, remaining);
|
||||
input.Read (output, dst, count);
|
||||
dst += count;
|
||||
}
|
||||
@ -262,7 +270,8 @@ namespace GameRes.Formats.Ankh
|
||||
if (0x6F == ctl)
|
||||
count = input.ReadUInt16();
|
||||
else
|
||||
count = ctl - 0x3D;
|
||||
count = (byte)(ctl - 0x3D);
|
||||
count = Math.Min (count, remaining);
|
||||
byte v = input.ReadUInt8();
|
||||
while (count --> 0)
|
||||
output[dst++] = v;
|
||||
@ -272,13 +281,14 @@ namespace GameRes.Formats.Ankh
|
||||
if (ctl == 0x9F)
|
||||
count = input.ReadUInt16();
|
||||
else
|
||||
count = ctl - 0x6E;
|
||||
byte v1 = input.ReadUInt8();
|
||||
byte v2 = input.ReadUInt8();
|
||||
while (count --> 0)
|
||||
count = (byte)(ctl - 0x6E);
|
||||
dst += input.Read (output, dst, Math.Min (2, remaining));
|
||||
--count;
|
||||
if (count > 0 && remaining > 2)
|
||||
{
|
||||
output[dst++] = v1;
|
||||
output[dst++] = v2;
|
||||
count = Math.Min (count * 2, remaining - 2);
|
||||
Binary.CopyOverlapped (output, dst-2, dst, count);
|
||||
dst += count;
|
||||
}
|
||||
}
|
||||
else if (ctl <= 0xBF)
|
||||
@ -286,26 +296,25 @@ namespace GameRes.Formats.Ankh
|
||||
if (ctl == 0xBF)
|
||||
count = input.ReadUInt16();
|
||||
else
|
||||
count = ctl - 0x9E;
|
||||
input.Read (output, dst, 3);
|
||||
if (count > 0)
|
||||
count = (byte)(ctl - 0x9E);
|
||||
dst += input.Read (output, dst, Math.Min (3, remaining));
|
||||
--count;
|
||||
if (count > 0 && remaining > 3)
|
||||
{
|
||||
count *= 3;
|
||||
Binary.CopyOverlapped (output, dst, dst+3, count-3);
|
||||
count = Math.Min (count * 3, remaining - 3);
|
||||
Binary.CopyOverlapped (output, dst-3, dst, count);
|
||||
dst += count;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
count = (ctl & 0x3F) + 3;
|
||||
count = Math.Min ((ctl & 0x3F) + 3, remaining);
|
||||
int offset = input.ReadUInt8();
|
||||
offset = (offset & 0x3F) - offsets[offset >> 6];
|
||||
Binary.CopyOverlapped (output, dst+offset, dst, count);
|
||||
dst += count;
|
||||
}
|
||||
}
|
||||
return new BinMemoryStream (output, entry.Name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -123,6 +123,11 @@
|
||||
<Compile Include="AdvSys\ImageGWD.cs" />
|
||||
<Compile Include="Ail\ArcLNK2.cs" />
|
||||
<Compile Include="AIRNovel\ArcAIR.cs" />
|
||||
<Compile Include="GScripter\ArcDATA.cs" />
|
||||
<Compile Include="Ism\ImagePNG.cs" />
|
||||
<Compile Include="Kogado\ArcARC.cs" />
|
||||
<Compile Include="Ice\ImageIBM.cs" />
|
||||
<Compile Include="Ice\ScriptISD.cs" />
|
||||
<Compile Include="BlueGale\ImageBBM.cs" />
|
||||
<Compile Include="ClickTeam\ArcMF.cs" />
|
||||
<Compile Include="DxLib\ArcDX8.cs" />
|
||||
@ -154,13 +159,19 @@
|
||||
<Compile Include="Apricot\ArcDAT.cs" />
|
||||
<Compile Include="ArcARCX.cs" />
|
||||
<Compile Include="Artemis\ArcMJA.cs" />
|
||||
<Compile Include="Macintosh\ImagePICT.cs" />
|
||||
<Compile Include="Macromedia\ArcDXR.cs" />
|
||||
<Compile Include="Macromedia\AudioSND.cs" />
|
||||
<Compile Include="Macromedia\DirectorFile.cs" />
|
||||
<Compile Include="Macromedia\Palettes.cs" />
|
||||
<Compile Include="Mugi\ArcBIN.cs" />
|
||||
<Compile Include="NScripter\Script.cs" />
|
||||
<Compile Include="ScrPlayer\ImageIMG.cs" />
|
||||
<Compile Include="Software House Parsley\ArcScn.cs" />
|
||||
<Compile Include="TechGian\ArcBIN.cs" />
|
||||
<Compile Include="Unity\ScriptDSM.cs" />
|
||||
<Compile Include="Zyx\ImageXMG.cs" />
|
||||
<None Include="AudioAIFF.cs" />
|
||||
<Compile Include="AudioAIFF.cs" />
|
||||
<Compile Include="Basil\ArcMIF.cs" />
|
||||
<Compile Include="Basil\AudioWHC.cs" />
|
||||
<Compile Include="Basil\ImageBCF.cs" />
|
||||
@ -624,7 +635,7 @@
|
||||
</Compile>
|
||||
<Compile Include="Glib2\ArcG2.cs" />
|
||||
<Compile Include="Glib2\ImagePGX.cs" />
|
||||
<Compile Include="ImagePSD.cs" />
|
||||
<Compile Include="Adobe\ImagePSD.cs" />
|
||||
<Compile Include="Kaguya\ArcANM.cs" />
|
||||
<Compile Include="Kaguya\ArcLINK.cs" />
|
||||
<Compile Include="Kaguya\ImageAO.cs" />
|
||||
@ -768,7 +779,7 @@
|
||||
<Compile Include="ArcIKS.cs" />
|
||||
<Compile Include="ArcPACKDAT.cs" />
|
||||
<Compile Include="CatSystem\ArcINT.cs" />
|
||||
<Compile Include="ArcISA.cs" />
|
||||
<Compile Include="Ism\ArcISA.cs" />
|
||||
<Compile Include="Kaas\ArcKAAS.cs" />
|
||||
<Compile Include="Kaguya\ArcKaguya.cs" />
|
||||
<Compile Include="Selene\ArcKCAP.cs" />
|
||||
@ -849,6 +860,10 @@
|
||||
<None Include="packages.config" />
|
||||
<EmbeddedResource Include="Strings\arcStrings.ja-JP.resx" />
|
||||
<EmbeddedResource Include="Unity\strings.dat" />
|
||||
<EmbeddedResource Include="ScrPlayer\ImgControlTable1" />
|
||||
<EmbeddedResource Include="ScrPlayer\ImgControlTable2" />
|
||||
<EmbeddedResource Include="ScrPlayer\ImgDeltaTable2" />
|
||||
<EmbeddedResource Include="ScrPlayer\ImgControlTable32" />
|
||||
<None Include="WebP\Alpha.cs" />
|
||||
<None Include="WebP\Decoder.cs">
|
||||
<SubType>Code</SubType>
|
||||
@ -986,7 +1001,7 @@
|
||||
<Compile Include="CatSystem\ImageHG3.cs" />
|
||||
<Compile Include="Triangle\ImageIAF.cs" />
|
||||
<Compile Include="Silky\ImageIGF.cs" />
|
||||
<Compile Include="ImageISG.cs" />
|
||||
<Compile Include="Ism\ImageISG.cs" />
|
||||
<Compile Include="Kaas\ImageKAAS.cs" />
|
||||
<Compile Include="MAI\ImageMAI.cs" />
|
||||
<Compile Include="FC01\ImageMCG.cs" />
|
||||
@ -1059,11 +1074,11 @@
|
||||
</Compile>
|
||||
<EmbeddedResource Include="Softpal\WaveTable1" />
|
||||
<EmbeddedResource Include="Softpal\WaveTable2" />
|
||||
<EmbeddedResource Include="ScrPlayer\ColorBitsTable1" />
|
||||
<EmbeddedResource Include="ScrPlayer\ColorBitsTable2" />
|
||||
<EmbeddedResource Include="ScrPlayer\ControlTable1" />
|
||||
<EmbeddedResource Include="ScrPlayer\ControlTable2" />
|
||||
<EmbeddedResource Include="ScrPlayer\ControlTable32" />
|
||||
<EmbeddedResource Include="ScrPlayer\IColorBitsTable1" />
|
||||
<EmbeddedResource Include="ScrPlayer\IColorBitsTable2" />
|
||||
<EmbeddedResource Include="ScrPlayer\IControlTable1" />
|
||||
<EmbeddedResource Include="ScrPlayer\IControlTable2" />
|
||||
<EmbeddedResource Include="ScrPlayer\IControlTable32" />
|
||||
<None Include="Resources\Formats.dat" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
|
@ -26,6 +26,7 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Linq;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
using GameRes.Utility;
|
||||
@ -302,33 +303,99 @@ namespace GameRes.Formats
|
||||
|
||||
public ResourceAccessor (string filename)
|
||||
{
|
||||
const uint LOAD_LIBRARY_AS_DATAFILE = 0x02;
|
||||
const uint LOAD_LIBRARY_AS_IMAGE_RESOURCE = 0x20;
|
||||
|
||||
m_exe = NativeMethods.LoadLibraryEx (filename, IntPtr.Zero, LOAD_LIBRARY_AS_IMAGE_RESOURCE);
|
||||
m_exe = NativeMethods.LoadLibraryEx (filename, IntPtr.Zero, LOAD_LIBRARY_AS_DATAFILE|LOAD_LIBRARY_AS_IMAGE_RESOURCE);
|
||||
if (IntPtr.Zero == m_exe)
|
||||
throw new Win32Exception (Marshal.GetLastWin32Error());
|
||||
}
|
||||
|
||||
public byte[] GetResource (string name, string type)
|
||||
{
|
||||
if (m_disposed)
|
||||
throw new ObjectDisposedException ("Access to disposed ResourceAccessor object failed.");
|
||||
var res = NativeMethods.FindResource (m_exe, name, type);
|
||||
var res = FindResource (name, type);
|
||||
if (IntPtr.Zero == res)
|
||||
return null;
|
||||
var glob = NativeMethods.LoadResource (m_exe, res);
|
||||
if (IntPtr.Zero == glob)
|
||||
return null;
|
||||
uint size = NativeMethods.SizeofResource (m_exe, res);
|
||||
var src = NativeMethods.LockResource (glob);
|
||||
var src = LockResource (res);
|
||||
if (IntPtr.Zero == src)
|
||||
return null;
|
||||
|
||||
uint size = NativeMethods.SizeofResource (m_exe, res);
|
||||
var dst = new byte[size];
|
||||
Marshal.Copy (src, dst, 0, dst.Length);
|
||||
return dst;
|
||||
}
|
||||
|
||||
public int ReadResource (string name, string type, byte[] dest, int pos)
|
||||
{
|
||||
var res = FindResource (name, type);
|
||||
if (IntPtr.Zero == res)
|
||||
return 0;
|
||||
var src = LockResource (res);
|
||||
if (IntPtr.Zero == src)
|
||||
return 0;
|
||||
int length = (int)NativeMethods.SizeofResource (m_exe, res);
|
||||
length = Math.Min (dest.Length - pos, length);
|
||||
Marshal.Copy (src, dest, pos, length);
|
||||
return length;
|
||||
}
|
||||
|
||||
public uint GetResourceSize (string name, string type)
|
||||
{
|
||||
var res = FindResource (name, type);
|
||||
if (IntPtr.Zero == res)
|
||||
return 0;
|
||||
return NativeMethods.SizeofResource (m_exe, res);
|
||||
}
|
||||
|
||||
private IntPtr FindResource (string name, string type)
|
||||
{
|
||||
if (m_disposed)
|
||||
throw new ObjectDisposedException ("Access to disposed ResourceAccessor object failed.");
|
||||
return NativeMethods.FindResource (m_exe, name, type);
|
||||
}
|
||||
|
||||
private IntPtr LockResource (IntPtr res)
|
||||
{
|
||||
var glob = NativeMethods.LoadResource (m_exe, res);
|
||||
if (IntPtr.Zero == glob)
|
||||
return IntPtr.Zero;
|
||||
return NativeMethods.LockResource (glob);
|
||||
}
|
||||
|
||||
public IEnumerable<string> EnumTypes ()
|
||||
{
|
||||
var types = new List<string>();
|
||||
if (!NativeMethods.EnumResourceTypes (m_exe, (m, t, p) => AddResourceName (types, t), IntPtr.Zero))
|
||||
return Enumerable.Empty<string>();
|
||||
return types;
|
||||
}
|
||||
|
||||
public IEnumerable<string> EnumNames (string type)
|
||||
{
|
||||
var names = new List<string>();
|
||||
if (!NativeMethods.EnumResourceNames (m_exe, type, (m, t, n, p) => AddResourceName (names, n), IntPtr.Zero))
|
||||
return Enumerable.Empty<string>();
|
||||
return names;
|
||||
}
|
||||
|
||||
private static bool AddResourceName (List<string> list, IntPtr name)
|
||||
{
|
||||
list.Add (ResourceNameToString (name));
|
||||
return true;
|
||||
}
|
||||
|
||||
private static string ResourceNameToString (IntPtr resName)
|
||||
{
|
||||
if ((resName.ToInt64() >> 16) == 0)
|
||||
{
|
||||
return "#" + resName.ToString();
|
||||
}
|
||||
else
|
||||
{
|
||||
return Marshal.PtrToStringUni (resName);
|
||||
}
|
||||
}
|
||||
|
||||
#region IDisposable implementation
|
||||
bool m_disposed = false;
|
||||
public void Dispose ()
|
||||
@ -363,7 +430,7 @@ namespace GameRes.Formats
|
||||
[return: MarshalAs(UnmanagedType.Bool)]
|
||||
static internal extern bool FreeLibrary (IntPtr hModule);
|
||||
|
||||
[DllImport( "kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
|
||||
[DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
|
||||
static internal extern IntPtr FindResource (IntPtr hModule, string lpName, string lpType);
|
||||
|
||||
[DllImport("Kernel32.dll", SetLastError = true)]
|
||||
@ -374,5 +441,22 @@ namespace GameRes.Formats
|
||||
|
||||
[DllImport("kernel32.dll")]
|
||||
static internal extern IntPtr LockResource (IntPtr hResData);
|
||||
|
||||
internal delegate bool EnumResTypeProc (IntPtr hModule, IntPtr lpszType, IntPtr lParam);
|
||||
internal delegate bool EnumResNameProc (IntPtr hModule, IntPtr lpszType, IntPtr lpszName, IntPtr lParam);
|
||||
internal delegate bool EnumResLangProc (IntPtr hModule, IntPtr lpszType, IntPtr lpszName, ushort wIDLanguage, IntPtr lParam);
|
||||
|
||||
[DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
|
||||
static internal extern bool EnumResourceTypes (IntPtr hModule, [MarshalAs(UnmanagedType.FunctionPtr)] EnumResTypeProc lpEnumFunc, IntPtr lParam);
|
||||
|
||||
[DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
|
||||
static internal extern bool EnumResourceNames (IntPtr hModule, IntPtr lpszType, EnumResNameProc lpEnumFunc, IntPtr lParam);
|
||||
[DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
|
||||
static internal extern bool EnumResourceNames (IntPtr hModule, string lpszType, EnumResNameProc lpEnumFunc, IntPtr lParam);
|
||||
|
||||
[DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
|
||||
static internal extern bool EnumResourceLanguages (IntPtr hModule, IntPtr lpszType, string lpName, EnumResLangProc lpEnumFunc, IntPtr lParam);
|
||||
[DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
|
||||
static internal extern bool EnumResourceLanguages (IntPtr hModule, string lpszType, string lpName, EnumResLangProc lpEnumFunc, IntPtr lParam);
|
||||
}
|
||||
}
|
||||
|
@ -2,7 +2,7 @@
|
||||
//! \date 2019 Jan 01
|
||||
//! \brief Family Adv System resource archive.
|
||||
//
|
||||
// Copyright (C) 2019 by morkt
|
||||
// Copyright (C) 2019-2023 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
|
||||
@ -34,6 +34,11 @@ using GameRes.Utility;
|
||||
|
||||
namespace GameRes.Formats.FamilyAdvSystem
|
||||
{
|
||||
[Serializable]
|
||||
public class FamilyAdvScheme : ResourceScheme
|
||||
{
|
||||
public IDictionary<string, string> KnownKeys;
|
||||
}
|
||||
|
||||
[Export(typeof(ArchiveFormat))]
|
||||
public class CsafOpener : ArchiveFormat
|
||||
@ -70,8 +75,9 @@ namespace GameRes.Formats.FamilyAdvSystem
|
||||
{
|
||||
if (is_encrypted)
|
||||
{
|
||||
var key = QueryEncryptionKey (file);
|
||||
file.View.Read (0x20, index, 0, index_size);
|
||||
enc = new CsafEncryption (DefaultKey, DefaultIV);
|
||||
enc = new CsafEncryption (key, DefaultIV);
|
||||
using (var decryptor = enc.CreateDecryptor (0))
|
||||
using (var enc_names = file.CreateStream (0x20 + index_size, names_size))
|
||||
using (var dec_names = new InputCryptoStream (enc_names, decryptor))
|
||||
@ -136,6 +142,27 @@ namespace GameRes.Formats.FamilyAdvSystem
|
||||
var input = new CsafStream (carc);
|
||||
return new StreamRegion (input, entry.Offset, entry.Size);
|
||||
}
|
||||
|
||||
internal string QueryEncryptionKey (ArcView file)
|
||||
{
|
||||
var title = FormatCatalog.Instance.LookupGame (file.Name);
|
||||
if (string.IsNullOrEmpty (title))
|
||||
return DefaultKey;
|
||||
string key;
|
||||
if (!KnownKeys.TryGetValue (title, out key))
|
||||
return DefaultKey;
|
||||
return key;
|
||||
}
|
||||
|
||||
FamilyAdvScheme DefaultScheme = new FamilyAdvScheme { KnownKeys = new Dictionary<string, string>() };
|
||||
|
||||
internal IDictionary<string, string> KnownKeys { get { return DefaultScheme.KnownKeys; } }
|
||||
|
||||
public override ResourceScheme Scheme
|
||||
{
|
||||
get { return DefaultScheme; }
|
||||
set { DefaultScheme = (FamilyAdvScheme)value; }
|
||||
}
|
||||
}
|
||||
|
||||
internal class CsafEncryption : IDisposable
|
||||
|
80
ArcFormats/GScripter/ArcDATA.cs
Normal file
80
ArcFormats/GScripter/ArcDATA.cs
Normal file
@ -0,0 +1,80 @@
|
||||
//! \file ArcDATA.cs
|
||||
//! \date 2023 Sep 06
|
||||
//! \brief GScripter resource archive.
|
||||
//
|
||||
// Copyright (C) 2023 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;
|
||||
|
||||
namespace GameRes.Formats.GScripter
|
||||
{
|
||||
[Export(typeof(ArchiveFormat))]
|
||||
public class DataOpener : ArchiveFormat
|
||||
{
|
||||
public override string Tag { get => "DAT/GScripter"; }
|
||||
public override string Description { get => "GScripter engine resource archive"; }
|
||||
public override uint Signature { get => 0; }
|
||||
public override bool IsHierarchic { get => false; }
|
||||
public override bool CanWrite { get => false; }
|
||||
|
||||
public DataOpener ()
|
||||
{
|
||||
Extensions = new[] { "" };
|
||||
}
|
||||
|
||||
public override ArcFile TryOpen (ArcView file)
|
||||
{
|
||||
var info_name = file.Name + ".info";
|
||||
if (!VFS.FileExists (info_name))
|
||||
return null;
|
||||
using (var index = VFS.OpenView (info_name))
|
||||
{
|
||||
int count = (int)index.MaxOffset / 0x28;
|
||||
if (!IsSaneCount (count) || count * 0x28 != index.MaxOffset)
|
||||
return null;
|
||||
var arc_name = Path.GetFileName (file.Name);
|
||||
bool is_cg = arc_name.StartsWith ("CG");
|
||||
bool is_sound = !is_cg && arc_name.StartsWith ("SOUND");
|
||||
uint index_pos = 0;
|
||||
var dir = new List<Entry> (count);
|
||||
for (int i = 0; i < count; ++i)
|
||||
{
|
||||
var name = index.View.ReadString (index_pos, 0x20);
|
||||
var entry = new Entry {
|
||||
Name = name,
|
||||
Type = is_cg ? "image" : is_sound ? "audio" : "",
|
||||
Offset = index.View.ReadUInt32 (index_pos+0x20),
|
||||
Size = index.View.ReadUInt32 (index_pos+0x24),
|
||||
};
|
||||
if (!entry.CheckPlacement (file.MaxOffset))
|
||||
return null;
|
||||
dir.Add (entry);
|
||||
index_pos += 0x28;
|
||||
}
|
||||
return new ArcFile (file, this, dir);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
88
ArcFormats/Ice/ImageIBM.cs
Normal file
88
ArcFormats/Ice/ImageIBM.cs
Normal file
@ -0,0 +1,88 @@
|
||||
//! \file ImageIBM.cs
|
||||
//! \date 2023 Aug 25
|
||||
//! \brief Ice Soft compressed bitmap format.
|
||||
//
|
||||
// Copyright (C) 2023 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.ComponentModel.Composition;
|
||||
using System.IO;
|
||||
using System.Windows.Media.Imaging;
|
||||
using GameRes.Formats.Ankh;
|
||||
|
||||
// [000324][Juice] Orgel ~Kesenai Melody~
|
||||
|
||||
namespace GameRes.Formats.Ice
|
||||
{
|
||||
internal class IbmMetaData : ImageMetaData
|
||||
{
|
||||
public int UnpackedSize;
|
||||
}
|
||||
|
||||
[Export(typeof(ImageFormat))]
|
||||
public class IbmFormat : ImageFormat
|
||||
{
|
||||
public override string Tag { get => "IBM/ICE"; }
|
||||
public override string Description { get => "Ice Soft compressed bitmap"; }
|
||||
public override uint Signature { get => 0x01575054; } // 'TPW'
|
||||
|
||||
public override ImageMetaData ReadMetaData (IBinaryStream file)
|
||||
{
|
||||
file.Position = 4;
|
||||
int unpacked_size = file.ReadInt32();
|
||||
if (unpacked_size <= 0)
|
||||
return null;
|
||||
var bmp_header = new byte[56];
|
||||
GrpOpener.UnpackTpw (file, bmp_header);
|
||||
using (var bmp = new BinMemoryStream (bmp_header, file.Name))
|
||||
{
|
||||
var bmp_info = Bmp.ReadMetaData (bmp);
|
||||
if (null == bmp_info)
|
||||
return null;
|
||||
return new IbmMetaData {
|
||||
Width = bmp_info.Width,
|
||||
Height = bmp_info.Height,
|
||||
BPP = bmp_info.BPP,
|
||||
UnpackedSize = unpacked_size,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
public override ImageData Read (IBinaryStream file, ImageMetaData info)
|
||||
{
|
||||
var meta = (IbmMetaData)info;
|
||||
var output = new byte[meta.UnpackedSize];
|
||||
GrpOpener.UnpackTpw (file, output);
|
||||
using (var bmp = new BinMemoryStream (output, file.Name))
|
||||
{
|
||||
var decoder = new BmpBitmapDecoder (bmp, BitmapCreateOptions.None, BitmapCacheOption.OnLoad);
|
||||
var frame = decoder.Frames[0];
|
||||
frame.Freeze();
|
||||
return new ImageData (frame, info);
|
||||
}
|
||||
}
|
||||
|
||||
public override void Write (Stream file, ImageData image)
|
||||
{
|
||||
throw new System.NotImplementedException ("IbmFormat.Write not implemented");
|
||||
}
|
||||
}
|
||||
}
|
58
ArcFormats/Ice/ScriptISD.cs
Normal file
58
ArcFormats/Ice/ScriptISD.cs
Normal file
@ -0,0 +1,58 @@
|
||||
//! \file ScriptISD.cs
|
||||
//! \date 2023 Aug 25
|
||||
//! \brief Ice Soft compressed binary script.
|
||||
//
|
||||
// Copyright (C) 2023 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.Formats.Ankh;
|
||||
using System.ComponentModel.Composition;
|
||||
using System.IO;
|
||||
|
||||
namespace GameRes.Formats.Ice
|
||||
{
|
||||
[Export(typeof(ScriptFormat))]
|
||||
public class IsdScript : GenericScriptFormat
|
||||
{
|
||||
public override string Tag { get => "ISD"; }
|
||||
public override string Description { get => "Ice Soft binary script"; }
|
||||
public override uint Signature { get => 0x01575054; } // 'TPW'
|
||||
|
||||
public override bool IsScript (IBinaryStream file)
|
||||
{
|
||||
return file.Signature == Signature;
|
||||
}
|
||||
|
||||
public override Stream ConvertFrom (IBinaryStream file)
|
||||
{
|
||||
file.Position = 4;
|
||||
int unpacked_size = file.ReadInt32();
|
||||
var data = new byte[unpacked_size];
|
||||
GrpOpener.UnpackTpw (file, data);
|
||||
return new BinMemoryStream (data, file.Name);
|
||||
}
|
||||
|
||||
public override Stream ConvertBack (IBinaryStream file)
|
||||
{
|
||||
throw new System.NotImplementedException();
|
||||
}
|
||||
}
|
||||
}
|
@ -36,12 +36,26 @@ namespace GameRes.Formats
|
||||
public override uint Signature { get { return 0; } }
|
||||
public override bool CanWrite { get { return true; } }
|
||||
|
||||
public MbImageFormat ()
|
||||
{
|
||||
Extensions = new[] { "bmp", "gra" };
|
||||
}
|
||||
|
||||
public override ImageMetaData ReadMetaData (IBinaryStream stream)
|
||||
{
|
||||
int c1 = stream.ReadByte();
|
||||
int c2 = stream.ReadByte();
|
||||
if ('M' != c1 || ('B' != c2 && 'C' != c2))
|
||||
switch (c1)
|
||||
{
|
||||
case 'M':
|
||||
if ('B' != c2 && 'C' != c2)
|
||||
return null;
|
||||
break;
|
||||
case 'C':
|
||||
if ('L' != c2)
|
||||
return null;
|
||||
break;
|
||||
}
|
||||
using (var bmp = OpenAsBitmap (stream))
|
||||
return Bmp.ReadMetaData (bmp);
|
||||
}
|
||||
|
@ -48,7 +48,7 @@ namespace GameRes.Formats.Interheart
|
||||
|
||||
public CandyFormat ()
|
||||
{
|
||||
Signatures = new uint[] { 0x0E00, 0 };
|
||||
Signatures = new uint[] { 0x0E00, 0x80020A00, 0 };
|
||||
}
|
||||
|
||||
public override ImageMetaData ReadMetaData (IBinaryStream file)
|
||||
@ -82,7 +82,7 @@ namespace GameRes.Formats.Interheart
|
||||
}
|
||||
else
|
||||
return null;
|
||||
if (info.Version != 1 && info.Version != 2 || info.BPP < 8 || info.BPP > 32)
|
||||
if (info.Version != 1 && info.Version != 2 || info.BPP < 1 || info.BPP > 32)
|
||||
return null;
|
||||
return info;
|
||||
}
|
||||
@ -91,7 +91,7 @@ namespace GameRes.Formats.Interheart
|
||||
{
|
||||
var reader = new CandyDecoder (file, (CandyMetaData)info);
|
||||
var pixels = reader.Unpack();
|
||||
return ImageData.Create (info, reader.Format, reader.Palette, pixels);
|
||||
return ImageData.Create (info, reader.Format, reader.Palette, pixels, reader.Stride);
|
||||
}
|
||||
|
||||
public override void Write (Stream file, ImageData image)
|
||||
@ -113,6 +113,7 @@ namespace GameRes.Formats.Interheart
|
||||
|
||||
public PixelFormat Format { get; private set; }
|
||||
public BitmapPalette Palette { get; private set; }
|
||||
public int Stride { get { return m_stride; } }
|
||||
|
||||
public CandyDecoder (IBinaryStream input, CandyMetaData info)
|
||||
{
|
||||
@ -133,6 +134,12 @@ namespace GameRes.Formats.Interheart
|
||||
m_stride = m_width * 4;
|
||||
Format = PixelFormats.Bgra32;
|
||||
}
|
||||
else if (1 == info.BPP)
|
||||
{
|
||||
m_pixel_size = 1;
|
||||
m_stride = (m_width + 7) / 8;
|
||||
Format = PixelFormats.Indexed1;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_pixel_size = 1;
|
||||
@ -148,6 +155,8 @@ namespace GameRes.Formats.Interheart
|
||||
if (m_colors > 0)
|
||||
Palette = ReadPalette();
|
||||
LzUnpack();
|
||||
if (1 == m_info.BPP)
|
||||
return m_output;
|
||||
return RestorePixels();
|
||||
}
|
||||
|
||||
|
@ -37,6 +37,11 @@ namespace GameRes.Formats.ISM
|
||||
public override bool IsHierarchic { get { return false; } }
|
||||
public override bool CanWrite { get { return false; } }
|
||||
|
||||
public IsaOpener ()
|
||||
{
|
||||
ContainedFormats = new[] { "ISG", "PNG/ISM", "OGG", };
|
||||
}
|
||||
|
||||
public override ArcFile TryOpen (ArcView file)
|
||||
{
|
||||
if (!file.View.AsciiEqual (4, "ARCHIVED"))
|
||||
@ -87,6 +92,13 @@ namespace GameRes.Formats.ISM
|
||||
entry.Size = m_file.View.ReadUInt32 (index_offset+8);
|
||||
if (!entry.CheckPlacement (m_file.MaxOffset))
|
||||
return null;
|
||||
if (string.IsNullOrEmpty (entry.Type) && name_length < 0x20) // try to fix truncated extension
|
||||
{
|
||||
if (name.EndsWith (".OG"))
|
||||
entry.Type = "audio";
|
||||
else if (name.EndsWith (".PN"))
|
||||
entry.Type = "image";
|
||||
}
|
||||
m_dir.Add (entry);
|
||||
index_offset += record_length;
|
||||
}
|
@ -41,6 +41,7 @@ namespace GameRes.Formats.ISM
|
||||
public int Colors;
|
||||
public uint Packed;
|
||||
public uint Unpacked;
|
||||
public int RecursionDepth;
|
||||
}
|
||||
|
||||
[Export(typeof(ImageFormat))]
|
||||
@ -78,88 +79,46 @@ namespace GameRes.Formats.ISM
|
||||
public override ImageData Read (IBinaryStream stream, ImageMetaData info)
|
||||
{
|
||||
var meta = (IsgMetaData)info;
|
||||
if (0x21 != meta.Type && 0x10 != meta.Type)
|
||||
if (0x21 != meta.Type && 0x10 != meta.Type && 0x34 != meta.Type)
|
||||
throw new InvalidFormatException ("Unsupported ISM image type");
|
||||
|
||||
stream.Position = 0x30;
|
||||
using (var input = new Reader (stream, meta))
|
||||
{
|
||||
if (0x21 == meta.Type)
|
||||
input.Unpack21();
|
||||
else
|
||||
input.Unpack10();
|
||||
var input = new Reader (stream, meta);
|
||||
var pixels = input.Unpack (this);
|
||||
var palette = new BitmapPalette (input.Palette);
|
||||
return ImageData.CreateFlipped (info, PixelFormats.Indexed8, palette, input.Data, (int)info.Width);
|
||||
}
|
||||
return ImageData.CreateFlipped (info, PixelFormats.Indexed8, palette, input.Data, info.iWidth);
|
||||
}
|
||||
|
||||
internal class Reader : IDisposable
|
||||
internal class Reader
|
||||
{
|
||||
IBinaryStream m_input;
|
||||
byte[] m_data;
|
||||
Color[] m_palette;
|
||||
int m_input_size;
|
||||
IsgMetaData m_info;
|
||||
|
||||
public Color[] Palette { get { return m_palette; } }
|
||||
public const int RecursionLimit = 32; // have seen 18 deep
|
||||
|
||||
public Color[] Palette { get; private set; }
|
||||
public byte[] Data { get { return m_data; } }
|
||||
|
||||
public Reader (IBinaryStream file, IsgMetaData info)
|
||||
{
|
||||
int palette_size = (int)info.Colors*4;
|
||||
var palette_data = new byte[Math.Max (0x400, palette_size)];
|
||||
if (palette_size != file.Read (palette_data, 0, palette_size))
|
||||
throw new InvalidFormatException();
|
||||
m_palette = new Color[0x100];
|
||||
for (int i = 0; i < m_palette.Length; ++i)
|
||||
{
|
||||
m_palette[i] = Color.FromRgb (palette_data[i*4+2], palette_data[i*4+1], palette_data[i*4]);
|
||||
}
|
||||
m_input = file;
|
||||
m_input_size = (int)info.Packed;
|
||||
m_data = new byte[info.Width * info.Height];
|
||||
m_info = info;
|
||||
}
|
||||
|
||||
public void Unpack21 ()
|
||||
public byte[] Unpack (IsgFormat isg)
|
||||
{
|
||||
int dst = 0;
|
||||
var frame = new byte[2048];
|
||||
int frame_pos = 2039;
|
||||
int remaining = m_input_size;
|
||||
byte ctl = m_input.ReadUInt8();
|
||||
--remaining;
|
||||
int bit = 0x80;
|
||||
while (remaining > 0)
|
||||
{
|
||||
if (0 != (ctl & bit))
|
||||
{
|
||||
byte hi = m_input.ReadUInt8();
|
||||
byte lo = m_input.ReadUInt8();
|
||||
remaining -= 2;
|
||||
int offset = (hi & 7) << 8 | lo;
|
||||
for (int count = (hi >> 3) + 3; count > 0; --count)
|
||||
{
|
||||
byte p = frame[offset];
|
||||
frame[frame_pos] = p;
|
||||
m_data[dst++] = p;
|
||||
offset = (offset + 1) & 0x7ff;
|
||||
frame_pos = (frame_pos + 1) & 0x7ff;
|
||||
}
|
||||
}
|
||||
m_input.Position = 0x30;
|
||||
if (0x34 == m_info.Type)
|
||||
return Unpack34(isg);
|
||||
Palette = ImageFormat.ReadColorMap (m_input.AsStream);
|
||||
m_data = new byte[m_info.Width * m_info.Height];
|
||||
if (0x21 == m_info.Type)
|
||||
DecompressLzss (m_input_size, m_data);
|
||||
else
|
||||
{
|
||||
byte p = m_input.ReadUInt8();
|
||||
--remaining;
|
||||
m_data[dst++] = p;
|
||||
frame[frame_pos] = p;
|
||||
frame_pos = (frame_pos + 1) & 0x7ff;
|
||||
}
|
||||
if (0 == (bit >>= 1))
|
||||
{
|
||||
ctl = m_input.ReadUInt8();
|
||||
--remaining;
|
||||
bit = 0x80;
|
||||
}
|
||||
}
|
||||
Unpack10();
|
||||
return m_data;
|
||||
}
|
||||
|
||||
public void Unpack10 ()
|
||||
@ -192,12 +151,112 @@ namespace GameRes.Formats.ISM
|
||||
}
|
||||
}
|
||||
|
||||
#region IDisposable Members
|
||||
public void Dispose ()
|
||||
public byte[] Unpack34 (IsgFormat isg)
|
||||
{
|
||||
GC.SuppressFinalize (this);
|
||||
if (m_info.RecursionDepth >= RecursionLimit)
|
||||
throw new InvalidFormatException ("Recursion limit reached for ISG image.");
|
||||
var base_name = m_input.ReadCString (0x10);
|
||||
var base_image = ReadBaseImage (base_name, isg);
|
||||
if (null == base_image)
|
||||
throw new InvalidFormatException ("Unable to read baseline ISG image.");
|
||||
int count = m_input.ReadInt32();
|
||||
int packed_size = m_input.ReadInt32();
|
||||
int ovl_ctl_size = m_info.iWidth * m_info.iHeight / 128;
|
||||
var overlay_info = m_input.ReadBytes (ovl_ctl_size);
|
||||
var overlay_data = new byte[count * 32 + 8];
|
||||
DecompressLzss (packed_size, overlay_data);
|
||||
int bit_count = 0;
|
||||
int ctl_src = 0;
|
||||
int data_src = 0;
|
||||
for (int y = 0; y < m_info.iHeight; y += 4)
|
||||
for (int x = 0; x < m_info.iWidth; x += 4)
|
||||
{
|
||||
if (((1 << bit_count) & overlay_info[ctl_src]) != 0)
|
||||
{
|
||||
int dst = y * m_info.iWidth + x;
|
||||
for (int r = 0; r < 4; ++r)
|
||||
{
|
||||
base_image[dst ] = overlay_data[data_src++];
|
||||
base_image[dst+1] = overlay_data[data_src++];
|
||||
base_image[dst+2] = overlay_data[data_src++];
|
||||
base_image[dst+3] = overlay_data[data_src++];
|
||||
dst += m_info.iWidth;
|
||||
}
|
||||
}
|
||||
if (++bit_count == 8)
|
||||
{
|
||||
bit_count = 0;
|
||||
++ctl_src;
|
||||
}
|
||||
}
|
||||
return m_data = base_image;
|
||||
}
|
||||
|
||||
internal void DecompressLzss (int remaining, byte[] output)
|
||||
{
|
||||
int dst = 0;
|
||||
var frame = new byte[2048];
|
||||
int frame_pos = 2039;
|
||||
byte ctl = m_input.ReadUInt8();
|
||||
--remaining;
|
||||
int bit = 0x80;
|
||||
while (remaining > 0)
|
||||
{
|
||||
if (0 != (ctl & bit))
|
||||
{
|
||||
byte hi = m_input.ReadUInt8();
|
||||
byte lo = m_input.ReadUInt8();
|
||||
remaining -= 2;
|
||||
int offset = (hi & 7) << 8 | lo;
|
||||
for (int count = (hi >> 3) + 3; count > 0; --count)
|
||||
{
|
||||
byte p = frame[offset];
|
||||
frame[frame_pos] = p;
|
||||
output[dst++] = p;
|
||||
offset = (offset + 1) & 0x7ff;
|
||||
frame_pos = (frame_pos + 1) & 0x7ff;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
byte p = m_input.ReadUInt8();
|
||||
--remaining;
|
||||
output[dst++] = p;
|
||||
frame[frame_pos] = p;
|
||||
frame_pos = (frame_pos + 1) & 0x7ff;
|
||||
}
|
||||
if (0 == (bit >>= 1))
|
||||
{
|
||||
ctl = m_input.ReadUInt8();
|
||||
--remaining;
|
||||
bit = 0x80;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal byte[] ReadBaseImage (string name, IsgFormat isg)
|
||||
{
|
||||
if (!VFS.FileExists (name))
|
||||
{
|
||||
if (name.Length <= 12)
|
||||
return null;
|
||||
name = name.Substring (0, 12);
|
||||
if (!VFS.FileExists (name))
|
||||
return null;
|
||||
}
|
||||
using (var base_file = VFS.OpenBinaryStream (name))
|
||||
{
|
||||
var base_info = isg.ReadMetaData (base_file) as IsgMetaData;
|
||||
if (null == base_info || base_info.Width != m_info.Width || base_info.Height != m_info.Height)
|
||||
throw new InvalidFormatException ("Invalid baseline ISG image.");
|
||||
base_info.RecursionDepth = m_info.RecursionDepth + 1;
|
||||
base_info.FileName = name;
|
||||
var reader = new Reader (base_file, base_info);
|
||||
var pixels = reader.Unpack (isg);
|
||||
this.Palette = reader.Palette;
|
||||
return pixels;
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
}
|
71
ArcFormats/Ism/ImagePNG.cs
Normal file
71
ArcFormats/Ism/ImagePNG.cs
Normal file
@ -0,0 +1,71 @@
|
||||
//! \file ImagePNG.cs
|
||||
//! \date 2023 Sep 02
|
||||
//! \brief PNG image with inverted alpha-channel.
|
||||
//
|
||||
// Copyright (C) 2023 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.ComponentModel.Composition;
|
||||
using System.IO;
|
||||
using System.Windows.Media;
|
||||
using System.Windows.Media.Imaging;
|
||||
|
||||
namespace GameRes.Formats.Ism
|
||||
{
|
||||
[Export(typeof(ImageFormat))]
|
||||
[ExportMetadata("Priority", -1)] // deprioritize
|
||||
public class PngIsmFormat : ImageFormat
|
||||
{
|
||||
public override string Tag { get => "PNG/ISM"; }
|
||||
public override string Description { get => "ISM engine PNG image"; }
|
||||
public override uint Signature { get => 0x474e5089; }
|
||||
|
||||
public override ImageMetaData ReadMetaData (IBinaryStream file)
|
||||
{
|
||||
// format only applied when extracting from related archive
|
||||
if (!VFS.IsVirtual || VFS.CurrentArchive.Tag != "ISA")
|
||||
return null;
|
||||
return Png.ReadMetaData (file);
|
||||
}
|
||||
|
||||
public override ImageData Read (IBinaryStream file, ImageMetaData info)
|
||||
{
|
||||
var decoder = new PngBitmapDecoder (file.AsStream,
|
||||
BitmapCreateOptions.None, BitmapCacheOption.OnLoad);
|
||||
BitmapSource bitmap = decoder.Frames[0];
|
||||
if (bitmap.Format != PixelFormats.Bgra32)
|
||||
return new ImageData (bitmap, info);
|
||||
int stride = bitmap.PixelWidth * 4;
|
||||
var pixels = new byte[stride * bitmap.PixelHeight];
|
||||
bitmap.CopyPixels (pixels, stride, 0);
|
||||
for (int i = 3; i < pixels.Length; i += 4)
|
||||
{
|
||||
pixels[i] ^= 0xFF;
|
||||
}
|
||||
return ImageData.Create (info, bitmap.Format, bitmap.Palette, pixels);
|
||||
}
|
||||
|
||||
public override void Write (Stream file, ImageData image)
|
||||
{
|
||||
throw new System.NotImplementedException ("PngIsmFormat.Write not implemented");
|
||||
}
|
||||
}
|
||||
}
|
@ -23,8 +23,11 @@
|
||||
// IN THE SOFTWARE.
|
||||
//
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.Composition;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Windows.Media;
|
||||
|
||||
namespace GameRes.Formats.Kaguya
|
||||
@ -98,4 +101,101 @@ namespace GameRes.Formats.Kaguya
|
||||
return ImageData.CreateFlipped (Info, format, null, pixels, stride);
|
||||
}
|
||||
}
|
||||
|
||||
internal class Pl10Entry : An21Entry
|
||||
{
|
||||
public ImageMetaData Info;
|
||||
}
|
||||
|
||||
[Export(typeof(ArchiveFormat))]
|
||||
public class Pl10Opener : An21Opener
|
||||
{
|
||||
public override string Tag { get => "PL10"; }
|
||||
public override uint Signature { get => 0x30314C50; } // 'PL10'
|
||||
|
||||
public Pl10Opener ()
|
||||
{
|
||||
Extensions = new string[] { "plt" };
|
||||
}
|
||||
|
||||
public override ArcFile TryOpen (ArcView file)
|
||||
{
|
||||
using (var input = file.CreateStream())
|
||||
{
|
||||
var base_info = GetBaseInfo (input);
|
||||
var dir = GetFramesList (input);
|
||||
if (null == dir)
|
||||
return null;
|
||||
string base_name = Path.GetFileNameWithoutExtension (file.Name);
|
||||
foreach (Pl10Entry entry in dir)
|
||||
{
|
||||
entry.Name = string.Format ("{0}#{1:D2}", base_name, entry.FrameIndex);
|
||||
entry.Type = "image";
|
||||
}
|
||||
var first = (Pl10Entry)dir[0];
|
||||
base_info.BPP = first.Info.BPP;
|
||||
return new An21Archive (file, this, dir, base_info);
|
||||
}
|
||||
}
|
||||
|
||||
internal ImageMetaData GetBaseInfo (IBinaryStream input)
|
||||
{
|
||||
input.Position = 6;
|
||||
return new ImageMetaData
|
||||
{
|
||||
OffsetX = input.ReadInt32(),
|
||||
OffsetY = input.ReadInt32(),
|
||||
Width = input.ReadUInt32(),
|
||||
Height = input.ReadUInt32(),
|
||||
};
|
||||
}
|
||||
|
||||
internal List<Entry> GetFramesList (IBinaryStream file)
|
||||
{
|
||||
file.Position = 4;
|
||||
int count = file.ReadInt16();
|
||||
if (!IsSaneCount (count))
|
||||
return null;
|
||||
var dir = new List<Entry> (count);
|
||||
long current_offset = 0x16;
|
||||
file.Position = current_offset;
|
||||
var frame_info = new ImageMetaData {
|
||||
OffsetX = file.ReadInt32(),
|
||||
OffsetY = file.ReadInt32(),
|
||||
Width = file.ReadUInt32(),
|
||||
Height = file.ReadUInt32(),
|
||||
BPP = file.ReadInt32() * 8,
|
||||
};
|
||||
uint depth = (uint)frame_info.BPP / 8;
|
||||
uint image_size = depth * frame_info.Width * frame_info.Height;
|
||||
var entry = new Pl10Entry
|
||||
{
|
||||
Offset = current_offset + 0x14,
|
||||
Size = image_size,
|
||||
FrameIndex = 0,
|
||||
RleStep = 0,
|
||||
Info = frame_info,
|
||||
};
|
||||
dir.Add (entry);
|
||||
for (int i = 1; i < count; ++i)
|
||||
{
|
||||
current_offset = entry.Offset + entry.Size;
|
||||
file.Position = current_offset;
|
||||
byte rle_step = file.ReadUInt8();
|
||||
uint packed_size = file.ReadUInt32();
|
||||
entry = new Pl10Entry
|
||||
{
|
||||
Offset = current_offset + 5,
|
||||
Size = packed_size,
|
||||
UnpackedSize = image_size,
|
||||
IsPacked = true,
|
||||
FrameIndex = i,
|
||||
RleStep = rle_step,
|
||||
Info = frame_info,
|
||||
};
|
||||
dir.Add (entry);
|
||||
}
|
||||
return dir;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
308
ArcFormats/Kogado/ArcARC.cs
Normal file
308
ArcFormats/Kogado/ArcARC.cs
Normal file
@ -0,0 +1,308 @@
|
||||
//! \file ArcARC.cs
|
||||
//! \date 2023 Aug 26
|
||||
//! \brief Kogado resource archive.
|
||||
//
|
||||
// Copyright (C) 2023 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.Compression;
|
||||
using GameRes.Formats.DirectDraw;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.Composition;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using System.Windows.Media;
|
||||
|
||||
// [070302][G-mode] Keitai Shoujo
|
||||
|
||||
namespace GameRes.Formats.Kogado
|
||||
{
|
||||
[Export(typeof(ArchiveFormat))]
|
||||
public class ArcOpener : ArchiveFormat
|
||||
{
|
||||
public override string Tag { get => "ARC/KOGADO"; }
|
||||
public override string Description { get => "Kogado engine resource archive"; }
|
||||
public override uint Signature { get => 0xA8BCADBE; } // 'ARCW' ^ 0xFFFFFFFF
|
||||
public override bool IsHierarchic { get => true; }
|
||||
public override bool CanWrite { get => false; }
|
||||
|
||||
public override ArcFile TryOpen (ArcView file)
|
||||
{
|
||||
using (var reader = new ArcIndexReader (file))
|
||||
{
|
||||
var dir = reader.ReadIndex();
|
||||
return new ArcFile(file, this, dir);
|
||||
}
|
||||
}
|
||||
|
||||
public override Stream OpenEntry (ArcFile arc, Entry entry)
|
||||
{
|
||||
Stream input = arc.File.CreateStream (entry.Offset, entry.Size);
|
||||
input = new XoredStream (input, 0xFF);
|
||||
var ova = entry as OvaEntry;
|
||||
if (ova != null)
|
||||
return new PrefixStream (ova.Header, input);
|
||||
var pent = entry as PackedEntry;
|
||||
if (null == pent || !pent.IsPacked)
|
||||
return input;
|
||||
return new LimitStream (new LzssStream (input), pent.UnpackedSize);
|
||||
}
|
||||
|
||||
public override IImageDecoder OpenImage (ArcFile arc, Entry entry)
|
||||
{
|
||||
var dds = entry as DdsEntry;
|
||||
if (null == dds)
|
||||
return base.OpenImage (arc, entry);
|
||||
var input = this.OpenEntry (arc, entry);
|
||||
return new ImageDecoder (input, dds.Info);
|
||||
}
|
||||
}
|
||||
|
||||
internal class OvaEntry : PackedEntry
|
||||
{
|
||||
public byte[] Header;
|
||||
}
|
||||
|
||||
internal class DdsEntry : PackedEntry
|
||||
{
|
||||
public DdsInfo Info;
|
||||
}
|
||||
|
||||
internal class DdsInfo : ImageMetaData
|
||||
{
|
||||
public DdsPF Flags;
|
||||
}
|
||||
|
||||
internal sealed class ArcIndexReader : IDisposable
|
||||
{
|
||||
IBinaryStream m_input;
|
||||
uint m_base_offset;
|
||||
long m_max_offset;
|
||||
|
||||
public ArcIndexReader (ArcView arc)
|
||||
{
|
||||
m_base_offset = arc.View.ReadUInt32 (0xC);
|
||||
m_max_offset = arc.MaxOffset;
|
||||
m_input = arc.CreateStream();
|
||||
}
|
||||
|
||||
byte[] m_filenames;
|
||||
byte[] m_index;
|
||||
List<Entry> m_dir;
|
||||
|
||||
public List<Entry> ReadIndex ()
|
||||
{
|
||||
m_input.Position = 0x10;
|
||||
m_filenames = ReadChunk();
|
||||
m_index = ReadChunk();
|
||||
m_dir = new List<Entry>();
|
||||
int section_count = m_index.ToInt32 (0);
|
||||
int pos = 4;
|
||||
for (int i = 0; i < section_count; ++i)
|
||||
{
|
||||
int count = m_index.ToInt32 (pos+8);
|
||||
if (m_dir.Capacity < m_dir.Count + count)
|
||||
m_dir.Capacity = m_dir.Count + count;
|
||||
int section_size = m_index.ToInt32 (pos+0xC);
|
||||
int name_pos = pos + 0x10;
|
||||
int layout_pos = name_pos + 4 * count;
|
||||
if (m_index.AsciiEqual (pos, "DDS\0"))
|
||||
ReadDdsSection (name_pos, layout_pos, count);
|
||||
else if (m_index.AsciiEqual (pos, "OVA\0"))
|
||||
ReadOvaSection (name_pos, layout_pos, count);
|
||||
else
|
||||
ReadSection (name_pos, layout_pos, count);
|
||||
pos += 0x10 + section_size;
|
||||
}
|
||||
return m_dir;
|
||||
}
|
||||
|
||||
void ReadSection (int name_pos, int layout_pos, int count)
|
||||
{
|
||||
for (int j = 0; j < count; ++j)
|
||||
{
|
||||
var name = ReadFileName (m_index.ToInt32 (name_pos));
|
||||
var entry = FormatCatalog.Instance.Create<PackedEntry> (name);
|
||||
|
||||
entry.Offset = m_index.ToUInt32 (layout_pos) + m_base_offset;
|
||||
entry.Size = m_index.ToUInt32 (layout_pos+4);
|
||||
entry.UnpackedSize = m_index.ToUInt32 (layout_pos+0xC);
|
||||
entry.IsPacked = true;
|
||||
if (!entry.CheckPlacement (m_max_offset+0x14))
|
||||
throw new InvalidFormatException();
|
||||
|
||||
m_dir.Add (entry);
|
||||
name_pos += 4;
|
||||
layout_pos += 0x10;
|
||||
}
|
||||
}
|
||||
|
||||
void ReadDdsSection (int name_pos, int layout_pos, int count)
|
||||
{
|
||||
int header_count = m_index.ToInt32 (layout_pos);
|
||||
var headers = new DdsInfo[header_count];
|
||||
layout_pos += 4;
|
||||
for (int i = 0; i < header_count; ++i)
|
||||
{
|
||||
headers[i] = new DdsInfo {
|
||||
Width = m_index.ToUInt32 (layout_pos+4),
|
||||
Height = m_index.ToUInt32 (layout_pos+8),
|
||||
BPP = 32,
|
||||
Flags = (DdsPF)m_index.ToUInt32 (layout_pos),
|
||||
};
|
||||
layout_pos += 0xC;
|
||||
}
|
||||
for (int j = 0; j < count; ++j)
|
||||
{
|
||||
var name = ReadFileName (m_index.ToInt32 (name_pos));
|
||||
var entry = FormatCatalog.Instance.Create<DdsEntry> (name);
|
||||
|
||||
entry.Offset = m_index.ToUInt32 (layout_pos) + m_base_offset;
|
||||
entry.Size = m_index.ToUInt32 (layout_pos+4);
|
||||
entry.UnpackedSize = m_index.ToUInt32 (layout_pos+0xC);
|
||||
int header_id = m_index.ToInt32 (layout_pos+0x10);
|
||||
entry.Info = headers[header_id];
|
||||
entry.IsPacked = true;
|
||||
if (!entry.CheckPlacement (m_max_offset+0x14))
|
||||
throw new InvalidFormatException();
|
||||
|
||||
m_dir.Add (entry);
|
||||
name_pos += 4;
|
||||
layout_pos += 0x14;
|
||||
}
|
||||
}
|
||||
|
||||
void ReadOvaSection (int name_pos, int layout_pos, int count)
|
||||
{
|
||||
int header_count = m_index.ToInt32 (layout_pos+4);
|
||||
var headers = new byte[header_count][];
|
||||
layout_pos += 8;
|
||||
for (int i = 0; i < header_count; ++i)
|
||||
{
|
||||
int header_len = m_index.ToInt32 (layout_pos+8);
|
||||
int header_pos = layout_pos + 12;
|
||||
headers[i] = new CowArray<byte> (m_index, header_pos, header_len).ToArray();
|
||||
layout_pos = header_pos + header_len;
|
||||
}
|
||||
for (int j = 0; j < count; ++j)
|
||||
{
|
||||
var name = ReadFileName (m_index.ToInt32 (name_pos));
|
||||
var entry = FormatCatalog.Instance.Create<OvaEntry> (name);
|
||||
|
||||
entry.Offset = m_index.ToUInt32 (layout_pos) + m_base_offset;
|
||||
entry.UnpackedSize = m_index.ToUInt32 (layout_pos+4);
|
||||
int header_id = m_index.ToInt32 (layout_pos+8);
|
||||
entry.Header = headers[header_id];
|
||||
entry.Size = entry.UnpackedSize - (uint)entry.Header.Length;
|
||||
entry.IsPacked = true;
|
||||
if (!entry.CheckPlacement (m_max_offset))
|
||||
throw new InvalidFormatException();
|
||||
|
||||
m_dir.Add (entry);
|
||||
name_pos += 4;
|
||||
layout_pos += 0xC;
|
||||
}
|
||||
}
|
||||
|
||||
string ReadFileName (int pos)
|
||||
{
|
||||
int i;
|
||||
for (i = pos; i+1 < m_filenames.Length; i += 2)
|
||||
{
|
||||
if (m_filenames[i] == 0 && m_filenames[i+1] == 0)
|
||||
break;
|
||||
}
|
||||
return Encoding.Unicode.GetString (m_filenames, pos, i - pos);
|
||||
}
|
||||
|
||||
byte[] ReadChunk ()
|
||||
{
|
||||
long start_pos = m_input.Position;
|
||||
int size = m_input.ReadInt32();
|
||||
int type = m_input.ReadInt32();
|
||||
int unpacked_size = m_input.ReadInt32();
|
||||
if (size <= 0 || unpacked_size <= 0)
|
||||
throw new InvalidFormatException();
|
||||
var data = new byte[unpacked_size];
|
||||
using (var decrypted = new XoredStream (m_input.AsStream, 0xFF, true))
|
||||
using (var lzss = new LzssStream (decrypted))
|
||||
{
|
||||
lzss.Read (data, 0, data.Length);
|
||||
}
|
||||
m_input.Position = start_pos + size;
|
||||
return data;
|
||||
}
|
||||
|
||||
#region IDisposable Members
|
||||
bool _disposed = false;
|
||||
|
||||
public void Dispose ()
|
||||
{
|
||||
if (!_disposed)
|
||||
{
|
||||
m_input.Dispose();
|
||||
_disposed = true;
|
||||
}
|
||||
GC.SuppressFinalize (this);
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
|
||||
internal sealed class ImageDecoder : IImageDecoder
|
||||
{
|
||||
Stream m_input;
|
||||
ImageData m_image;
|
||||
|
||||
public Stream Source => m_input;
|
||||
public ImageFormat SourceFormat => null;
|
||||
public PixelFormat Format { get; private set; }
|
||||
public ImageMetaData Info { get; private set; }
|
||||
public ImageData Image => m_image ?? (m_image = GetImageData());
|
||||
|
||||
public ImageDecoder (Stream input, DdsInfo info)
|
||||
{
|
||||
m_input = input;
|
||||
Info = info;
|
||||
Format = info.Flags.HasFlag (DdsPF.AlphaPixels) ? PixelFormats.Bgra32 : PixelFormats.Bgr32;
|
||||
}
|
||||
|
||||
private ImageData GetImageData ()
|
||||
{
|
||||
var pixels = new byte[Info.iWidth * Info.iHeight * 4];
|
||||
m_input.Read (pixels, 0, pixels.Length);
|
||||
return ImageData.Create (Info, Format, null, pixels);
|
||||
}
|
||||
|
||||
#region IDisposable members
|
||||
bool m_disposed = false;
|
||||
public void Dispose ()
|
||||
{
|
||||
if (!m_disposed)
|
||||
{
|
||||
m_input.Dispose();
|
||||
m_disposed = true;
|
||||
}
|
||||
GC.SuppressFinalize (this);
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
436
ArcFormats/Macintosh/ImagePICT.cs
Normal file
436
ArcFormats/Macintosh/ImagePICT.cs
Normal file
@ -0,0 +1,436 @@
|
||||
//! \file ImagePICT.cs
|
||||
//! \date 2023 Aug 24
|
||||
//! \brief Macintosh picture format.
|
||||
//
|
||||
// Copyright (C) 2023 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.Apple
|
||||
{
|
||||
internal class PictMetaData : ImageMetaData
|
||||
{
|
||||
public uint DataOffset;
|
||||
}
|
||||
|
||||
[Export(typeof(ImageFormat))]
|
||||
public class PictFormat : ImageFormat
|
||||
{
|
||||
public override string Tag { get => "PICT/MAC"; }
|
||||
public override string Description { get => "Apple Macintosh image format"; }
|
||||
public override uint Signature { get => 0; }
|
||||
|
||||
public PictFormat ()
|
||||
{
|
||||
Signatures = new[] { 0u, 0x54434950u };
|
||||
Extensions = new[] { "pct", "pict", "pic" };
|
||||
}
|
||||
|
||||
public override ImageMetaData ReadMetaData (IBinaryStream file)
|
||||
{
|
||||
int header_pos = 0x200;
|
||||
if (file.Signature == 0x54434950) // 'PICT'
|
||||
header_pos = 4;
|
||||
if (file.Length < header_pos + 0x10)
|
||||
return null;
|
||||
file.Position = header_pos + 2;
|
||||
short top = file.ReadI16BE();
|
||||
short left = file.ReadI16BE();
|
||||
short bottom = file.ReadI16BE();
|
||||
short right = file.ReadI16BE();
|
||||
if (file.ReadU16BE() != 0x11)
|
||||
return null;
|
||||
int version = file.ReadU16BE();
|
||||
if (version != 0x2FF)
|
||||
return null;
|
||||
return new PictMetaData {
|
||||
Width = (uint)(right - left),
|
||||
Height = (uint)(bottom - top),
|
||||
OffsetX = left,
|
||||
OffsetY = top,
|
||||
BPP = 32,
|
||||
DataOffset = (uint)file.Position,
|
||||
};
|
||||
}
|
||||
|
||||
public override ImageData Read (IBinaryStream file, ImageMetaData info)
|
||||
{
|
||||
var decoder = new PictReader (file, (PictMetaData)info);
|
||||
var pixels = decoder.Unpack();
|
||||
return ImageData.Create (info, decoder.Format, decoder.Palette, pixels);
|
||||
}
|
||||
|
||||
public override void Write (Stream file, ImageData image)
|
||||
{
|
||||
throw new System.NotImplementedException ("PictFormat.Write not implemented");
|
||||
}
|
||||
}
|
||||
|
||||
internal static class BinaryStreamExtension
|
||||
{
|
||||
static public short ReadI16BE (this IBinaryStream file)
|
||||
{
|
||||
return Binary.BigEndian (file.ReadInt16());
|
||||
}
|
||||
|
||||
static public ushort ReadU16BE (this IBinaryStream file)
|
||||
{
|
||||
return Binary.BigEndian (file.ReadUInt16());
|
||||
}
|
||||
|
||||
static public int ReadI32BE (this IBinaryStream file)
|
||||
{
|
||||
return Binary.BigEndian (file.ReadInt32());
|
||||
}
|
||||
|
||||
static public uint ReadU32BE (this IBinaryStream file)
|
||||
{
|
||||
return Binary.BigEndian (file.ReadUInt32());
|
||||
}
|
||||
}
|
||||
|
||||
internal class Pixmap
|
||||
{
|
||||
public short Version;
|
||||
public short PackType;
|
||||
public int PackSize;
|
||||
public int HorizRes;
|
||||
public int VertRes;
|
||||
public short PixelType;
|
||||
public short BPP;
|
||||
public short CompCount;
|
||||
public short CompSize;
|
||||
public int PlaneBytes;
|
||||
public int Table;
|
||||
|
||||
public void Deserialize (IBinaryStream input)
|
||||
{
|
||||
Version = input.ReadI16BE();
|
||||
PackType = input.ReadI16BE();
|
||||
PackSize = input.ReadI32BE();
|
||||
HorizRes = input.ReadI32BE() >> 16; // read 2 bytes and skip next 2
|
||||
VertRes = input.ReadI32BE() >> 16;
|
||||
PixelType = input.ReadI16BE();
|
||||
BPP = input.ReadI16BE();
|
||||
CompCount = input.ReadI16BE();
|
||||
CompSize = input.ReadI16BE();
|
||||
PlaneBytes = input.ReadI32BE();
|
||||
Table = input.ReadI32BE();
|
||||
input.Seek (4, SeekOrigin.Current);
|
||||
if (BPP <= 0 || BPP > 32 || CompCount <= 0 || CompCount > 4 || CompSize <= 0)
|
||||
throw new InvalidFormatException();
|
||||
}
|
||||
}
|
||||
|
||||
internal class PictReader
|
||||
{
|
||||
IBinaryStream m_input;
|
||||
PictMetaData m_info;
|
||||
|
||||
public PixelFormat Format { get; private set; }
|
||||
public BitmapPalette Palette { get; private set; }
|
||||
|
||||
public PictReader (IBinaryStream input, PictMetaData info)
|
||||
{
|
||||
m_input = input;
|
||||
m_info = info;
|
||||
}
|
||||
|
||||
bool HasAlpha = false;
|
||||
byte[] m_buffer;
|
||||
|
||||
public byte[] Unpack ()
|
||||
{
|
||||
Color[] colormap = null;
|
||||
Pixmap pixmap = null;
|
||||
m_input.Position = m_info.DataOffset;
|
||||
|
||||
while (m_input.PeekByte() != -1)
|
||||
{
|
||||
if ((m_input.Position & 1) != 0)
|
||||
Skip (1);
|
||||
int code = m_input.ReadU16BE();
|
||||
if (0x00FF == code || 0xFFFF == code) // EOF
|
||||
break;
|
||||
switch (code)
|
||||
{
|
||||
case 0x0000: // NOP
|
||||
continue;
|
||||
|
||||
case 0x0001: // Clip
|
||||
{
|
||||
int length = m_input.ReadU16BE();
|
||||
if (length < 2)
|
||||
throw new InvalidFormatException();
|
||||
Skip (length-2);
|
||||
break;
|
||||
}
|
||||
|
||||
case 0x0090:
|
||||
case 0x0091:
|
||||
case 0x0098:
|
||||
case 0x0099:
|
||||
case 0x009A:
|
||||
case 0x009B: // BitsRect
|
||||
{
|
||||
int stride = 0;
|
||||
if (code != 0x9A && code != 0x9B)
|
||||
stride = m_input.ReadU16BE();
|
||||
else
|
||||
Skip (6);
|
||||
// FIXME we just read the first bitmap and override an existing frame
|
||||
// TODO place bitmap into frame according to its RECT
|
||||
m_info.OffsetY = m_input.ReadI16BE();
|
||||
m_info.OffsetX = m_input.ReadI16BE();
|
||||
m_info.Height = (uint)(m_input.ReadI16BE() - m_info.OffsetY);
|
||||
m_info.Width = (uint)(m_input.ReadI16BE() - m_info.OffsetX);
|
||||
if (0x9A == code || 0x9B == code || (stride & 0x8000) != 0)
|
||||
{
|
||||
pixmap = new Pixmap();
|
||||
pixmap.Deserialize (m_input);
|
||||
HasAlpha = pixmap.CompCount == 4;
|
||||
}
|
||||
if (code != 0x9A && code != 0x9B)
|
||||
{
|
||||
int colors = 2;
|
||||
int flags = 0;
|
||||
if ((stride & 0x8000) != 0)
|
||||
{
|
||||
Skip (4);
|
||||
flags = m_input.ReadU16BE();
|
||||
colors = m_input.ReadU16BE() + 1;
|
||||
}
|
||||
if (null == colormap)
|
||||
colormap = new Color[colors];
|
||||
if ((stride & 0x8000) != 0)
|
||||
{
|
||||
for (int i = 0; i < colors; i++)
|
||||
{
|
||||
int c = m_input.ReadU16BE() % colors;
|
||||
if ((flags & 0x8000) != 0)
|
||||
c = i;
|
||||
int r = m_input.ReadU16BE() / 0x101;
|
||||
int g = m_input.ReadU16BE() / 0x101;
|
||||
int b = m_input.ReadU16BE() / 0x101;
|
||||
colormap[c] = Color.FromRgb ((byte)r, (byte)g, (byte)b);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
var White = Color.FromRgb (0xFF, 0xFF, 0xFF);
|
||||
for (int i = 0; i < colors; i++)
|
||||
{
|
||||
colormap[i] = Color.Subtract (White, colormap[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
Skip (8+8+2);
|
||||
// -> Skip (8); // source RECT
|
||||
// Skip (8); // destination RECT
|
||||
// Skip (2); // transfer mode
|
||||
if (code == 0x91 || code == 0x99 || code == 0x9b)
|
||||
{
|
||||
int length = m_input.ReadU16BE();
|
||||
if (length > 2)
|
||||
Skip (length - 2);
|
||||
}
|
||||
if (code != 0x9A && code != 0x9B && (stride & 0x8000) == 0)
|
||||
DecodeRleBitmap (stride, 1);
|
||||
else
|
||||
DecodeRleBitmap (stride, pixmap.BPP);
|
||||
break;
|
||||
}
|
||||
|
||||
case 0x00A1: // LongComment
|
||||
{
|
||||
m_input.ReadU16BE(); // comment type
|
||||
int length = m_input.ReadU16BE();
|
||||
Skip (length);
|
||||
break;
|
||||
}
|
||||
case 0x0C00: // Header
|
||||
Skip (0x18);
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new NotSupportedException (string.Format ("Unknown code 0x{0:X4} in PICT stream.", code));
|
||||
}
|
||||
}
|
||||
if (colormap != null)
|
||||
Palette = new BitmapPalette (colormap);
|
||||
if (null == m_buffer)
|
||||
throw new InvalidFormatException();
|
||||
SetFormat (pixmap);
|
||||
return RepackPixels (pixmap);
|
||||
}
|
||||
|
||||
byte[] RepackPixels (Pixmap pixmap)
|
||||
{
|
||||
int bpp = m_info.BPP;
|
||||
if (bpp <= 16)
|
||||
return m_buffer;
|
||||
int bytes_per_pixel = bpp / 8;
|
||||
int stride = m_info.iWidth * bytes_per_pixel;
|
||||
var pixels = new byte[stride * m_info.iHeight];
|
||||
int src = 0;
|
||||
for (int y = 0; y < m_info.iHeight; ++y)
|
||||
{
|
||||
int dst = y * stride;
|
||||
for (int x = 0; x < m_info.iWidth; ++x)
|
||||
{
|
||||
if (HasAlpha)
|
||||
{
|
||||
pixels[dst+3] = m_buffer[src];
|
||||
pixels[dst+2] = m_buffer[src+m_info.iWidth];
|
||||
pixels[dst+1] = m_buffer[src+m_info.iWidth*2];
|
||||
pixels[dst] = m_buffer[src+m_info.iWidth*3];
|
||||
}
|
||||
else
|
||||
{
|
||||
pixels[dst+2] = m_buffer[src];
|
||||
pixels[dst+1] = m_buffer[src+m_info.iWidth];
|
||||
pixels[dst] = m_buffer[src+m_info.iWidth*2];
|
||||
}
|
||||
++src;
|
||||
dst += bytes_per_pixel;
|
||||
}
|
||||
src += (pixmap.CompCount - 1) * m_info.iWidth;
|
||||
}
|
||||
return pixels;
|
||||
}
|
||||
|
||||
void SetFormat (Pixmap pixmap)
|
||||
{
|
||||
int bpp = null == pixmap ? 8 : pixmap.BPP;
|
||||
if (32 == bpp)
|
||||
{
|
||||
if (4 == pixmap.CompCount)
|
||||
Format = PixelFormats.Bgra32;
|
||||
else
|
||||
Format = PixelFormats.Bgr32;
|
||||
}
|
||||
else if (24 == bpp)
|
||||
Format = PixelFormats.Bgr24;
|
||||
else if (16 == bpp)
|
||||
Format = PixelFormats.Bgr555;
|
||||
else if (8 == bpp)
|
||||
{
|
||||
if (Palette != null)
|
||||
Format = PixelFormats.Indexed8;
|
||||
else
|
||||
Format = PixelFormats.Gray8;
|
||||
}
|
||||
else
|
||||
throw new NotSupportedException (string.Format ("Not supported PICT bitdepth -- {0}bpp", bpp));
|
||||
m_info.BPP = bpp;
|
||||
}
|
||||
|
||||
void Skip (int amount)
|
||||
{
|
||||
m_input.Seek (amount, SeekOrigin.Current);
|
||||
}
|
||||
|
||||
byte[] m_unpack_buffer = new byte[0x800];
|
||||
byte[] m_scanline;
|
||||
|
||||
void DecodeRleBitmap (int stride, int bpp)
|
||||
{
|
||||
if (bpp < 8)
|
||||
throw new NotSupportedException();
|
||||
if (bpp <= 8)
|
||||
stride &= 0x7fff;
|
||||
int width = m_info.iWidth;
|
||||
int bytes_per_pixel = 1;
|
||||
if (16 == bpp)
|
||||
{
|
||||
bytes_per_pixel = 2;
|
||||
width *= 2;
|
||||
}
|
||||
else if (32 == bpp)
|
||||
width *= HasAlpha ? 4 : 3;
|
||||
if (stride == 0)
|
||||
stride = width;
|
||||
int stride_32bpp = m_info.iWidth * 4;
|
||||
|
||||
int total_bytes = stride_32bpp * m_info.iHeight;
|
||||
if (null == m_buffer || m_buffer.Length < total_bytes)
|
||||
m_buffer = new byte[total_bytes];
|
||||
int scanline_length = stride_32bpp * 2;
|
||||
if (null == m_scanline || m_scanline.Length < scanline_length)
|
||||
{
|
||||
m_scanline = new byte[scanline_length];
|
||||
}
|
||||
if (stride < 8)
|
||||
{
|
||||
int dst = 0;
|
||||
int row_size = width * (bpp / 8);
|
||||
for (int y = 0; y < m_info.iHeight; ++y)
|
||||
{
|
||||
m_input.Read (m_buffer, dst, stride);
|
||||
dst += row_size;
|
||||
}
|
||||
return;
|
||||
}
|
||||
for (int y = 0; y < m_info.iHeight; ++y)
|
||||
{
|
||||
int dst = y * width;
|
||||
if (stride > 200)
|
||||
scanline_length = m_input.ReadU16BE();
|
||||
else
|
||||
scanline_length = m_input.ReadUInt8();
|
||||
if (scanline_length >= m_scanline.Length || scanline_length == 0)
|
||||
throw new InvalidFormatException();
|
||||
m_input.Read (m_scanline, 0, scanline_length);
|
||||
for (int j = 0; j < scanline_length; )
|
||||
{
|
||||
if ((m_scanline[j] & 0x80) == 0)
|
||||
{
|
||||
int pixel_count = m_scanline[j] + 1;
|
||||
int count = pixel_count * bytes_per_pixel;
|
||||
int src = j + 1;
|
||||
if ((dst + count) <= total_bytes)
|
||||
Buffer.BlockCopy (m_scanline, src, m_buffer, dst, count);
|
||||
dst += count;
|
||||
j += count + 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
int count = (m_scanline[j] ^ 0xFF) + 2;
|
||||
int src = j + 1;
|
||||
while (count --> 0)
|
||||
{
|
||||
if ((dst + bytes_per_pixel) <= total_bytes)
|
||||
Buffer.BlockCopy (m_scanline, src, m_buffer, dst, bytes_per_pixel);
|
||||
dst += bytes_per_pixel;
|
||||
}
|
||||
j += bytes_per_pixel + 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -51,7 +51,7 @@ namespace GameRes.Formats.Macromedia
|
||||
}
|
||||
|
||||
internal static readonly HashSet<string> RawChunks = new HashSet<string> {
|
||||
"RTE0", "RTE1", "FXmp", "VWFI", "VWSC", "Lscr", "STXT",
|
||||
"RTE0", "RTE1", "FXmp", "VWFI", "VWSC", "Lscr", "STXT", "XMED", "snd "
|
||||
};
|
||||
|
||||
internal bool ConvertText = true;
|
||||
@ -73,9 +73,11 @@ namespace GameRes.Formats.Macromedia
|
||||
ImportMedia (dir_file, dir);
|
||||
foreach (MemoryMapEntry entry in dir_file.MMap.Dir)
|
||||
{
|
||||
if (RawChunks.Contains (entry.FourCC))
|
||||
if (entry.Size != 0 && RawChunks.Contains (entry.FourCC))
|
||||
{
|
||||
entry.Name = string.Format ("{0:D6}.{1}", entry.Id, entry.FourCC.Trim());
|
||||
if ("snd " == entry.FourCC)
|
||||
entry.Type = "audio";
|
||||
dir.Add (entry);
|
||||
}
|
||||
}
|
||||
@ -98,6 +100,8 @@ namespace GameRes.Formats.Macromedia
|
||||
|
||||
internal Stream OpenSound (ArcFile arc, SoundEntry entry)
|
||||
{
|
||||
if (null == entry.Header)
|
||||
return base.OpenEntry (arc, entry);
|
||||
var header = arc.File.View.ReadBytes (entry.Header.Offset, entry.Header.Size);
|
||||
var format = entry.DeserializeHeader (header);
|
||||
var riff = new MemoryStream (0x2C);
|
||||
@ -147,11 +151,30 @@ namespace GameRes.Formats.Macromedia
|
||||
|
||||
Entry ImportSound (CastMember sound, DirectorFile dir_file)
|
||||
{
|
||||
var sndHrec = dir_file.KeyTable.FindByCast (sound.Id, "sndH");
|
||||
var sndSrec = dir_file.KeyTable.FindByCast (sound.Id, "sndS");
|
||||
var name = sound.Info.Name;
|
||||
KeyTableEntry sndHrec = null, sndSrec = null;
|
||||
foreach (var elem in dir_file.KeyTable.Table.Where (e => e.CastId == sound.Id))
|
||||
{
|
||||
if ("ediM" == elem.FourCC)
|
||||
{
|
||||
var ediM = dir_file.MMap[elem.Id];
|
||||
if (string.IsNullOrEmpty (name))
|
||||
name = ediM.Id.ToString ("D6");
|
||||
return new Entry
|
||||
{
|
||||
Name = name + ".mp3",
|
||||
Type = "audio",
|
||||
Offset = ediM.Offset,
|
||||
Size = ediM.Size,
|
||||
};
|
||||
}
|
||||
if (null == sndHrec && "sndH" == elem.FourCC)
|
||||
sndHrec = elem;
|
||||
else if (null == sndSrec && "sndS" == elem.FourCC)
|
||||
sndSrec = elem;
|
||||
}
|
||||
if (sndHrec == null || sndSrec == null)
|
||||
return null;
|
||||
var name = sound.Info.Name;
|
||||
var sndH = dir_file.MMap[sndHrec.Id];
|
||||
var sndS = dir_file.MMap[sndSrec.Id];
|
||||
if (string.IsNullOrEmpty (name))
|
||||
@ -168,7 +191,15 @@ namespace GameRes.Formats.Macromedia
|
||||
|
||||
Entry ImportBitmap (CastMember bitmap, DirectorFile dir_file, Cast cast)
|
||||
{
|
||||
var bitd = dir_file.KeyTable.FindByCast (bitmap.Id, "BITD");
|
||||
// var bitd = dir_file.KeyTable.FindByCast (bitmap.Id, "BITD");
|
||||
KeyTableEntry bitd = null, alfa = null;
|
||||
foreach (var elem in dir_file.KeyTable.Table.Where (e => e.CastId == bitmap.Id))
|
||||
{
|
||||
if (null == bitd && "BITD" == elem.FourCC)
|
||||
bitd = elem;
|
||||
else if (null == alfa && "ALFA" == elem.FourCC)
|
||||
alfa = elem;
|
||||
}
|
||||
if (bitd == null)
|
||||
return null;
|
||||
var entry = new BitmapEntry();
|
||||
@ -188,6 +219,8 @@ namespace GameRes.Formats.Macromedia
|
||||
if (clut != null)
|
||||
entry.PaletteRef = dir_file.MMap[clut.Id];
|
||||
}
|
||||
if (alfa != null)
|
||||
entry.AlphaRef = dir_file.MMap[alfa.Id];
|
||||
return entry;
|
||||
}
|
||||
|
||||
@ -214,13 +247,27 @@ namespace GameRes.Formats.Macromedia
|
||||
case -101: palette = Palettes.SystemWindows; break;
|
||||
}
|
||||
}
|
||||
var input = arc.File.CreateStream (entry.Offset, entry.Size);
|
||||
var info = new ImageMetaData {
|
||||
Width = (uint)(bent.Right - bent.Left),
|
||||
Height = (uint)(bent.Bottom - bent.Top),
|
||||
BPP = bent.BitDepth
|
||||
};
|
||||
return new BitdDecoder (input.AsStream, info, palette);
|
||||
byte[] alpha_channel = null;
|
||||
if (bent.AlphaRef != null)
|
||||
{
|
||||
using (var alpha = arc.File.CreateStream (bent.AlphaRef.Offset, bent.AlphaRef.Size))
|
||||
{
|
||||
var alpha_info = new ImageMetaData {
|
||||
Width = info.Width,
|
||||
Height = info.Height,
|
||||
BPP = 8,
|
||||
};
|
||||
var decoder = new BitdDecoder (alpha, alpha_info, null);
|
||||
alpha_channel = decoder.Unpack8bpp();
|
||||
}
|
||||
}
|
||||
var input = arc.File.CreateStream (entry.Offset, entry.Size);
|
||||
return new BitdDecoder (input.AsStream, info, palette) { AlphaChannel = alpha_channel };
|
||||
}
|
||||
|
||||
BitmapPalette ReadPalette (byte[] data)
|
||||
@ -246,6 +293,7 @@ namespace GameRes.Formats.Macromedia
|
||||
public int BitDepth;
|
||||
public int Palette;
|
||||
public Entry PaletteRef;
|
||||
public Entry AlphaRef;
|
||||
|
||||
public void DeserializeHeader (byte[] data)
|
||||
{
|
||||
|
@ -330,8 +330,8 @@ namespace GameRes.Formats.Macromedia
|
||||
{
|
||||
int rsize = m_bits.GetBits (5);
|
||||
m_dim.X = GetSignedBits (rsize);
|
||||
m_dim.Y = GetSignedBits (rsize);
|
||||
m_dim.Width = GetSignedBits (rsize) - m_dim.X;
|
||||
m_dim.Y = GetSignedBits (rsize);
|
||||
m_dim.Height = GetSignedBits (rsize) - m_dim.Y;
|
||||
}
|
||||
|
||||
|
112
ArcFormats/Macromedia/AudioSND.cs
Normal file
112
ArcFormats/Macromedia/AudioSND.cs
Normal file
@ -0,0 +1,112 @@
|
||||
//! \file AudioSND.cs
|
||||
//! \date 2023 Aug 30
|
||||
//! \brief Macromedia Director WAVE audio resource.
|
||||
//
|
||||
// Copyright (C) 2023 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;
|
||||
|
||||
namespace GameRes.Formats.Macromedia
|
||||
{
|
||||
[Export(typeof(AudioFormat))]
|
||||
public class SndAudio : AudioFormat
|
||||
{
|
||||
public override string Tag { get => "SND"; }
|
||||
public override string Description { get => "Macromedia Director audio resource"; }
|
||||
public override uint Signature { get => 0; }
|
||||
public override bool CanWrite { get => false; }
|
||||
|
||||
public override SoundInput TryOpen (IBinaryStream file)
|
||||
{
|
||||
if (!file.Name.HasExtension (".snd"))
|
||||
return null;
|
||||
int type = file.ReadUInt16();
|
||||
if (type != 0x0200)
|
||||
return null;
|
||||
var reader = new Reader (file.AsStream, ByteOrder.BigEndian);
|
||||
reader.Skip (2);
|
||||
int count = reader.ReadU16();
|
||||
if (0 == count)
|
||||
return null;
|
||||
int command = reader.ReadU16();
|
||||
if (command != 0x8051)
|
||||
return null;
|
||||
reader.ReadI16();
|
||||
int pos = reader.ReadI32();
|
||||
if (pos != reader.Position)
|
||||
return null;
|
||||
ushort channels = 1;
|
||||
ushort bps = 8;
|
||||
reader.Skip (4);
|
||||
int param = reader.ReadI32();
|
||||
ushort sample_rate = reader.ReadU16();
|
||||
reader.Skip (10);
|
||||
byte encoding = reader.ReadU8();
|
||||
byte freq = reader.ReadU8();
|
||||
if (freq != 0x3C)
|
||||
return null;
|
||||
int frames_count = 0;
|
||||
if (0 == encoding)
|
||||
{
|
||||
frames_count = param / channels;
|
||||
}
|
||||
else if (0xFF == encoding)
|
||||
{
|
||||
channels = (ushort)param;
|
||||
frames_count = reader.ReadI32();
|
||||
reader.Skip (22);
|
||||
bps = reader.ReadU16();
|
||||
reader.Skip (14);
|
||||
}
|
||||
else
|
||||
throw new NotSupportedException (string.Format ("Not supported 'snd' encoding {0:X2}", encoding));
|
||||
if (bps != 16 && bps != 8)
|
||||
return null;
|
||||
|
||||
var format = new WaveFormat {
|
||||
FormatTag = 1,
|
||||
Channels = channels,
|
||||
SamplesPerSecond = sample_rate,
|
||||
BlockAlign = (ushort)(bps / 8),
|
||||
BitsPerSample = bps,
|
||||
};
|
||||
format.SetBPS();
|
||||
if (8 == bps)
|
||||
{
|
||||
var data = new StreamRegion (file.AsStream, file.Position);
|
||||
return new RawPcmInput (data, format);
|
||||
}
|
||||
int sample_count = frames_count * channels;
|
||||
var samples = file.ReadBytes (sample_count);
|
||||
for (int i = 1; i < samples.Length; i += 2)
|
||||
{
|
||||
byte s = samples[i-1];
|
||||
samples[i-1] = samples[i];
|
||||
samples[i] = s;
|
||||
}
|
||||
var raw = new BinMemoryStream (samples);
|
||||
file.Dispose();
|
||||
return new RawPcmInput (raw, format);
|
||||
}
|
||||
}
|
||||
}
|
@ -215,11 +215,12 @@ namespace GameRes.Formats.Macromedia
|
||||
ImageData m_image;
|
||||
BitmapPalette m_palette;
|
||||
|
||||
public Stream Source { get => m_input; }
|
||||
public ImageFormat SourceFormat { get => null; }
|
||||
public ImageMetaData Info { get => m_info; }
|
||||
public ImageData Image { get => m_image ?? (m_image = GetImageData()); }
|
||||
public Stream Source => m_input;
|
||||
public ImageFormat SourceFormat => null;
|
||||
public ImageMetaData Info => m_info;
|
||||
public ImageData Image => m_image ?? (m_image = GetImageData());
|
||||
public PixelFormat Format { get; private set; }
|
||||
public byte[] AlphaChannel { get; set; }
|
||||
|
||||
public BitdDecoder (Stream input, ImageMetaData info, BitmapPalette palette)
|
||||
{
|
||||
@ -230,7 +231,8 @@ namespace GameRes.Formats.Macromedia
|
||||
m_stride = (m_width * m_info.BPP + 7) / 8;
|
||||
m_stride = (m_stride + 1) & ~1;
|
||||
m_output = new byte[m_stride * m_height];
|
||||
Format = info.BPP == 4 ? PixelFormats.Indexed4
|
||||
Format = info.BPP == 2 ? PixelFormats.Indexed2
|
||||
: info.BPP == 4 ? PixelFormats.Indexed4
|
||||
: info.BPP == 8 ? PixelFormats.Indexed8
|
||||
: info.BPP == 16 ? PixelFormats.Bgr555
|
||||
: PixelFormats.Bgr32;
|
||||
@ -239,14 +241,46 @@ namespace GameRes.Formats.Macromedia
|
||||
|
||||
protected ImageData GetImageData ()
|
||||
{
|
||||
m_input.Position = 0;
|
||||
if (Info.BPP <= 8)
|
||||
Unpack8bpp();
|
||||
else
|
||||
UnpackChannels (Info.BPP / 8);
|
||||
if (AlphaChannel != null)
|
||||
{
|
||||
if (Info.BPP != 32)
|
||||
{
|
||||
BitmapSource bitmap = BitmapSource.Create (Info.iWidth, Info.iHeight, ImageData.DefaultDpiX, ImageData.DefaultDpiY, Format, m_palette, m_output, m_stride);
|
||||
bitmap = new FormatConvertedBitmap (bitmap, PixelFormats.Bgr32, null, 0);
|
||||
m_stride = bitmap.PixelWidth * 4;
|
||||
m_output = new byte[bitmap.PixelHeight * m_stride];
|
||||
bitmap.CopyPixels (m_output, m_stride, 0);
|
||||
}
|
||||
ApplyAlphaChannel (AlphaChannel);
|
||||
Format = PixelFormats.Bgra32;
|
||||
}
|
||||
return ImageData.Create (m_info, Format, m_palette, m_output, m_stride);
|
||||
}
|
||||
|
||||
void Unpack8bpp ()
|
||||
void ApplyAlphaChannel (byte[] alpha)
|
||||
{
|
||||
int alpha_stride = (m_width + 1) & ~1;
|
||||
int src = 0;
|
||||
int pdst = 3;
|
||||
for (int y = 0; y < m_height; ++y)
|
||||
{
|
||||
int dst = pdst;
|
||||
for (int x = 0; x < m_width; ++x)
|
||||
{
|
||||
m_output[dst] = alpha[src+x];
|
||||
dst += 4;
|
||||
}
|
||||
src += alpha_stride;
|
||||
pdst += m_stride;
|
||||
}
|
||||
}
|
||||
|
||||
public byte[] Unpack8bpp ()
|
||||
{
|
||||
for (int line = 0; line < m_output.Length; line += m_stride)
|
||||
{
|
||||
@ -277,6 +311,7 @@ namespace GameRes.Formats.Macromedia
|
||||
}
|
||||
}
|
||||
}
|
||||
return m_output;
|
||||
}
|
||||
|
||||
public void UnpackChannels (int channels)
|
||||
|
94
ArcFormats/Mugi/ArcBIN.cs
Normal file
94
ArcFormats/Mugi/ArcBIN.cs
Normal file
@ -0,0 +1,94 @@
|
||||
//! \file ArcBIN.cs
|
||||
//! \date 2023 Sep 03
|
||||
//! \brief Mugi's resource archive.
|
||||
//
|
||||
// Copyright (C) 2023 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.Compression;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.Composition;
|
||||
using System.IO;
|
||||
|
||||
// [070323][schoolzone] Cosplay! Kyonyuu Mahjong
|
||||
|
||||
namespace GameRes.Formats.Mugi
|
||||
{
|
||||
[Export(typeof(ArchiveFormat))]
|
||||
public class BinOpener : ArchiveFormat
|
||||
{
|
||||
public override string Tag { get => "BIN/MUGI"; }
|
||||
public override string Description { get => "Mugi's resource archive"; }
|
||||
public override uint Signature { get => 0; }
|
||||
public override bool IsHierarchic { get => false; }
|
||||
public override bool CanWrite { get => false; }
|
||||
|
||||
public override ArcFile TryOpen (ArcView file)
|
||||
{
|
||||
const uint index_size = 0x8000 + 0x4000;
|
||||
if (file.MaxOffset <= index_size)
|
||||
return null;
|
||||
file.View.Reserve (0, index_size);
|
||||
uint index_pos = 0x8000;
|
||||
uint offset = file.View.ReadUInt32 (index_pos);
|
||||
if (offset != index_size)
|
||||
return null;
|
||||
uint[] offsets = new uint[0x800];
|
||||
int count = 0;
|
||||
while (offset != file.MaxOffset)
|
||||
{
|
||||
if (count == offsets.Length)
|
||||
return null;
|
||||
offsets[count++] = offset;
|
||||
index_pos += 4;
|
||||
offset = file.View.ReadUInt32 (index_pos);
|
||||
if (offset < offsets[count-1] || offset > file.MaxOffset)
|
||||
return null;
|
||||
}
|
||||
offsets[count--] = offset;
|
||||
uint name_pos = 0;
|
||||
uint size_pos = 0xA000;
|
||||
var dir = new List<Entry> (count);
|
||||
for (int i = 0; i < count; ++i)
|
||||
{
|
||||
var name = file.View.ReadString (name_pos, 0x10);
|
||||
var entry = Create<PackedEntry> (name);
|
||||
entry.Offset = offsets[i];
|
||||
entry.Size = (uint)(offsets[i+1] - offsets[i]);
|
||||
entry.UnpackedSize = file.View.ReadUInt32 (size_pos);
|
||||
entry.IsPacked = entry.Size != entry.UnpackedSize;
|
||||
dir.Add (entry);
|
||||
name_pos += 0x10;
|
||||
size_pos += 4;
|
||||
}
|
||||
return new ArcFile (file, this, dir);
|
||||
}
|
||||
|
||||
public override Stream OpenEntry (ArcFile arc, Entry entry)
|
||||
{
|
||||
var pent = (PackedEntry)entry;
|
||||
Stream input = arc.File.CreateStream (entry.Offset, entry.Size);
|
||||
if (pent.IsPacked)
|
||||
input = new LzssStream (input);
|
||||
return input;
|
||||
}
|
||||
}
|
||||
}
|
@ -126,16 +126,32 @@ namespace GameRes.Formats.Qlie
|
||||
}
|
||||
var enc = QlieEncryption.Create (file, index.PackVersion, arc_key);
|
||||
List<Entry> dir = null;
|
||||
try
|
||||
if (index.PackVersion.Major > 1)
|
||||
{
|
||||
dir = index.Read (enc, key_file, use_pack_keyfile);
|
||||
}
|
||||
catch
|
||||
else
|
||||
{
|
||||
if (index.PackVersion.Major == 1)
|
||||
// PackVer1.0 is a total mess -- it could either use
|
||||
// • V1 index layout and V1 encryption
|
||||
// • V1 index layout and V2 encryption
|
||||
// • V2 index layout and V2 encryption
|
||||
// all with the same 'FilePackVer1.0' signature
|
||||
var possibleEncs = new IEncryption[] {
|
||||
enc, new EncryptionV2 (IndexLayout.WithoutHash), new EncryptionV2()
|
||||
};
|
||||
foreach (var v1enc in possibleEncs)
|
||||
{
|
||||
enc = new EncryptionV2();
|
||||
dir = index.Read (enc, key_file, use_pack_keyfile);
|
||||
try
|
||||
{
|
||||
dir = index.Read (v1enc, key_file, use_pack_keyfile);
|
||||
if (dir != null)
|
||||
{
|
||||
enc = v1enc;
|
||||
break;
|
||||
}
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
}
|
||||
if (null == dir)
|
||||
@ -400,6 +416,8 @@ namespace GameRes.Formats.Qlie
|
||||
for (int i = 0; i < m_count; ++i)
|
||||
{
|
||||
int name_length = m_index.ReadUInt16();
|
||||
if (name_length > 0x100) // invalid encryption version
|
||||
return null;
|
||||
if (enc.IsUnicode)
|
||||
name_length *= 2;
|
||||
if (name_length > m_name_buffer.Length)
|
||||
@ -418,7 +436,7 @@ namespace GameRes.Formats.Qlie
|
||||
entry.UnpackedSize = m_index.ReadUInt32(); // [+0C]
|
||||
entry.IsPacked = 0 != m_index.ReadInt32(); // [+10]
|
||||
entry.EncryptionMethod = m_index.ReadInt32(); // [+14]
|
||||
if (m_pack_version.Major > 1)
|
||||
if (enc.IndexLayout == IndexLayout.WithHash)
|
||||
entry.Hash = m_index.ReadUInt32(); // [+18]
|
||||
entry.KeyFile = key_file;
|
||||
if (read_pack_keyfile && entry.Name.Contains ("pack_keyfile"))
|
||||
|
@ -33,6 +33,8 @@ namespace GameRes.Formats.Qlie
|
||||
{
|
||||
bool IsUnicode { get; }
|
||||
|
||||
IndexLayout IndexLayout { get; }
|
||||
|
||||
uint CalculateHash (byte[] data, int length);
|
||||
|
||||
string DecryptName (byte[] name, int name_length);
|
||||
@ -40,9 +42,17 @@ namespace GameRes.Formats.Qlie
|
||||
void DecryptEntry (byte[] data, int offset, int length, QlieEntry entry);
|
||||
}
|
||||
|
||||
internal enum IndexLayout
|
||||
{
|
||||
WithoutHash,
|
||||
WithHash
|
||||
}
|
||||
|
||||
internal abstract class QlieEncryption : IEncryption
|
||||
{
|
||||
public virtual bool IsUnicode { get { return false; } }
|
||||
public virtual bool IsUnicode => false;
|
||||
|
||||
public virtual IndexLayout IndexLayout => IndexLayout.WithHash;
|
||||
|
||||
/// <summary>
|
||||
/// Hash generated from the key data contained within archive index.
|
||||
@ -77,6 +87,8 @@ namespace GameRes.Formats.Qlie
|
||||
|
||||
internal class EncryptionV1 : QlieEncryption
|
||||
{
|
||||
public override IndexLayout IndexLayout => IndexLayout.WithoutHash;
|
||||
|
||||
public EncryptionV1 ()
|
||||
{
|
||||
NameKey = 0xC4;
|
||||
@ -126,10 +138,15 @@ namespace GameRes.Formats.Qlie
|
||||
|
||||
internal class EncryptionV2 : QlieEncryption
|
||||
{
|
||||
public EncryptionV2 ()
|
||||
public override IndexLayout IndexLayout => m_layout;
|
||||
|
||||
private IndexLayout m_layout;
|
||||
|
||||
public EncryptionV2 (IndexLayout layout = IndexLayout.WithHash)
|
||||
{
|
||||
NameKey = 0xC4;
|
||||
ArcKey = 0;
|
||||
m_layout = layout;
|
||||
}
|
||||
|
||||
public override uint CalculateHash (byte[] data, int length)
|
||||
|
@ -278,11 +278,11 @@ namespace GameRes.Formats.ScrPlayer
|
||||
#endregion
|
||||
|
||||
#region Bitmap tables
|
||||
static readonly Lazy<byte[]> s_control_table1 = new Lazy<byte[]> (() => LoadResource ("ControlTable1"));
|
||||
static readonly Lazy<byte[]> s_control_table2 = new Lazy<byte[]> (() => LoadResource ("ControlTable2"));
|
||||
static readonly Lazy<byte[]> s_control_table32 = new Lazy<byte[]> (() => LoadResource ("ControlTable32"));
|
||||
static readonly Lazy<byte[]> s_color_bits1 = new Lazy<byte[]> (() => LoadResource ("ColorBitsTable1"));
|
||||
static readonly Lazy<byte[]> s_color_bits2 = new Lazy<byte[]> (() => LoadResource ("ColorBitsTable2"));
|
||||
static readonly Lazy<byte[]> s_control_table1 = new Lazy<byte[]> (() => LoadResource ("IControlTable1"));
|
||||
static readonly Lazy<byte[]> s_control_table2 = new Lazy<byte[]> (() => LoadResource ("IControlTable2"));
|
||||
static readonly Lazy<byte[]> s_control_table32 = new Lazy<byte[]> (() => LoadResource ("IControlTable32"));
|
||||
static readonly Lazy<byte[]> s_color_bits1 = new Lazy<byte[]> (() => LoadResource ("IColorBitsTable1"));
|
||||
static readonly Lazy<byte[]> s_color_bits2 = new Lazy<byte[]> (() => LoadResource ("IColorBitsTable2"));
|
||||
|
||||
static byte[] ControlTable1 { get { return s_control_table1.Value; } }
|
||||
static byte[] ControlTable2 { get { return s_control_table2.Value; } }
|
||||
@ -390,7 +390,7 @@ namespace GameRes.Formats.ScrPlayer
|
||||
0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02,
|
||||
}
|
||||
};
|
||||
static readonly int[] OffsetTable = {
|
||||
internal static readonly int[] OffsetTable = {
|
||||
-1, 0, 0, 1, 1, 1, -1, 1, 2, 1, -2, 1, -2, 0, 0, 2, 1, 2, -1, 2, -3, 0,
|
||||
};
|
||||
#endregion
|
||||
|
490
ArcFormats/ScrPlayer/ImageIMG.cs
Normal file
490
ArcFormats/ScrPlayer/ImageIMG.cs
Normal file
@ -0,0 +1,490 @@
|
||||
//! \file ImageIMG.cs
|
||||
//! \date 2023 Aug 30
|
||||
//! \brief ScrPlayer image format.
|
||||
//
|
||||
// Copyright (C) 2023 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;
|
||||
|
||||
// [990709][Love Gun] Koi no Sweet Tart wa Ikaga
|
||||
|
||||
namespace GameRes.Formats.ScrPlayer
|
||||
{
|
||||
[Export(typeof(ImageFormat))]
|
||||
public class ImgFormat : ImageFormat
|
||||
{
|
||||
public override string Tag { get => "IMG"; }
|
||||
public override string Description { get => "ScrPlayer image format"; }
|
||||
public override uint Signature { get => 0x20474D49; } // 'IMG '
|
||||
|
||||
public override ImageMetaData ReadMetaData (IBinaryStream file)
|
||||
{
|
||||
var header = file.ReadHeader (0x18);
|
||||
int bpp = header.ToUInt16 (0x10);
|
||||
if (bpp != 24 && bpp != 32)
|
||||
return null;
|
||||
return new ImageMetaData {
|
||||
Width = header.ToUInt16 (0xC),
|
||||
Height = header.ToUInt16 (0xE),
|
||||
BPP = bpp,
|
||||
};
|
||||
}
|
||||
|
||||
public override ImageData Read (IBinaryStream file, ImageMetaData info)
|
||||
{
|
||||
using (var reader = new ImgReader (file, info))
|
||||
return reader.Unpack();
|
||||
}
|
||||
|
||||
public override void Write (Stream file, ImageData image)
|
||||
{
|
||||
throw new System.NotImplementedException ("ImgFormat.Write not implemented");
|
||||
}
|
||||
}
|
||||
|
||||
internal sealed class ImgReader : IDisposable
|
||||
{
|
||||
ImgBitStream m_input;
|
||||
ImageMetaData m_info;
|
||||
int m_stride;
|
||||
byte[] m_output;
|
||||
|
||||
public PixelFormat Format { get; private set; }
|
||||
|
||||
public ImgReader (IBinaryStream input, ImageMetaData info)
|
||||
{
|
||||
m_input = new ImgBitStream (input.AsStream, true);
|
||||
m_info = info;
|
||||
m_stride = m_info.iWidth * 4;
|
||||
m_output = new byte[m_stride * (m_info.iHeight + 1)];
|
||||
Format = m_info.BPP == 32 ? PixelFormats.Bgra32 : PixelFormats.Bgr32;
|
||||
}
|
||||
|
||||
public ImageData Unpack ()
|
||||
{
|
||||
m_input.Input.Position = 0x18;
|
||||
if (32 == m_info.BPP)
|
||||
Unpack32bpp();
|
||||
else
|
||||
Unpack24bpp();
|
||||
return ImageData.Create (m_info, Format, null, m_output);
|
||||
}
|
||||
|
||||
byte[] m_row_buffer = new byte[0x2580];
|
||||
int[] m_rows_ptr = new int[3] { 0, 0xC80, 0xC80 * 2 };
|
||||
|
||||
void Unpack24bpp ()
|
||||
{
|
||||
var offset_table = Img2Reader.OffsetTable.Clone() as int[];
|
||||
int dst = 0;
|
||||
for (int y = 0; y < m_info.iHeight; ++y)
|
||||
{
|
||||
int row_pos = m_rows_ptr[2];
|
||||
m_rows_ptr[2] = m_rows_ptr[1];
|
||||
m_rows_ptr[1] = m_rows_ptr[0];
|
||||
m_rows_ptr[0] = row_pos;
|
||||
for (int x = 0; x < m_info.iWidth; )
|
||||
{
|
||||
int ctl = m_input.GetBits (ControlTable1, 13);
|
||||
if (0xEF == ctl)
|
||||
ctl += m_input.GetBits (ControlTable2, 11);
|
||||
|
||||
int pos = m_input.GetBits (PosTable24, 5) * 2;
|
||||
int x_offset = offset_table[pos];
|
||||
int y_offset = offset_table[pos + 1];
|
||||
if (pos > 0)
|
||||
{
|
||||
offset_table[pos] = offset_table[pos-2];
|
||||
offset_table[pos+1] = offset_table[pos-1];
|
||||
offset_table[pos-2] = x_offset;
|
||||
offset_table[pos-1] = y_offset;
|
||||
}
|
||||
int src = m_rows_ptr[y_offset] + (x + x_offset) * 4;
|
||||
if (ctl >= 0xD8)
|
||||
{
|
||||
int count = ctl - 0xD6;
|
||||
Binary.CopyOverlapped (m_row_buffer, src, row_pos, count * 4);
|
||||
row_pos += count * 4;
|
||||
x += count;
|
||||
}
|
||||
else
|
||||
{
|
||||
switch (ColorCode[ctl])
|
||||
{
|
||||
case 0:
|
||||
m_row_buffer[row_pos + 2] = (byte)(m_row_buffer[src + 2] - MapRed[ctl]);
|
||||
m_row_buffer[row_pos + 1] = (byte)(m_row_buffer[src + 1] - MapGreen[ctl]);
|
||||
m_row_buffer[row_pos ] = (byte)(m_row_buffer[src ] - MapBlue[ctl]);
|
||||
break;
|
||||
case 1:
|
||||
m_row_buffer[row_pos + 2] = (byte)(m_row_buffer[src + 2] - GetDelta());
|
||||
m_row_buffer[row_pos + 1] = (byte)(m_row_buffer[src + 1] - MapGreen[ctl]);
|
||||
m_row_buffer[row_pos ] = (byte)(m_row_buffer[src ] - MapBlue[ctl]);
|
||||
break;
|
||||
case 2:
|
||||
m_row_buffer[row_pos + 2] = (byte)(m_row_buffer[src + 2] - MapRed[ctl]);
|
||||
m_row_buffer[row_pos + 1] = (byte)(m_row_buffer[src + 1] - GetDelta());
|
||||
m_row_buffer[row_pos ] = (byte)(m_row_buffer[src ] - MapBlue[ctl]);
|
||||
break;
|
||||
case 3:
|
||||
m_row_buffer[row_pos + 2] = (byte)(m_row_buffer[src + 2] - GetDelta());
|
||||
m_row_buffer[row_pos + 1] = (byte)(m_row_buffer[src + 1] - GetDelta());
|
||||
m_row_buffer[row_pos ] = (byte)(m_row_buffer[src ] - MapBlue[ctl]);
|
||||
break;
|
||||
case 4:
|
||||
m_row_buffer[row_pos + 2] = (byte)(m_row_buffer[src + 2] - MapRed[ctl]);
|
||||
m_row_buffer[row_pos + 1] = (byte)(m_row_buffer[src + 1] - MapGreen[ctl]);
|
||||
m_row_buffer[row_pos ] = (byte)(m_row_buffer[src ] - GetDelta());
|
||||
break;
|
||||
case 5:
|
||||
m_row_buffer[row_pos + 2] = (byte)(m_row_buffer[src + 2] - GetDelta());
|
||||
m_row_buffer[row_pos + 1] = (byte)(m_row_buffer[src + 1] - MapGreen[ctl]);
|
||||
m_row_buffer[row_pos ] = (byte)(m_row_buffer[src ] - GetDelta());
|
||||
break;
|
||||
case 6:
|
||||
m_row_buffer[row_pos + 2] = (byte)(m_row_buffer[src + 2] - MapRed[ctl]);
|
||||
m_row_buffer[row_pos + 1] = (byte)(m_row_buffer[src + 1] - GetDelta());
|
||||
m_row_buffer[row_pos ] = (byte)(m_row_buffer[src ] - GetDelta());
|
||||
break;
|
||||
case 7:
|
||||
m_row_buffer[row_pos + 2] = (byte)(m_row_buffer[src + 2] - GetDelta());
|
||||
m_row_buffer[row_pos + 1] = (byte)(m_row_buffer[src + 1] - GetDelta());
|
||||
m_row_buffer[row_pos ] = (byte)(m_row_buffer[src ] - GetDelta());
|
||||
break;
|
||||
}
|
||||
row_pos += 4;
|
||||
++x;
|
||||
}
|
||||
}
|
||||
Buffer.BlockCopy (m_row_buffer, m_rows_ptr[0], m_output, dst, m_stride);
|
||||
dst += m_stride;
|
||||
}
|
||||
}
|
||||
|
||||
void Unpack32bpp ()
|
||||
{
|
||||
for (int i = 3; i < m_row_buffer.Length; i += 4)
|
||||
m_row_buffer[i] = 0xFF;
|
||||
var offset_table = Img2Reader.OffsetTable.Clone() as int[];
|
||||
int dst = 0;
|
||||
for (int y = 0; y < m_info.iHeight; ++y)
|
||||
{
|
||||
int row_pos = m_rows_ptr[2];
|
||||
m_rows_ptr[2] = m_rows_ptr[1];
|
||||
m_rows_ptr[1] = m_rows_ptr[0];
|
||||
m_rows_ptr[0] = row_pos;
|
||||
for (int x = 0; x < m_info.iWidth; )
|
||||
{
|
||||
int ctl = m_input.GetBits (ControlTable1, 13);
|
||||
if (0xEF == ctl)
|
||||
ctl += m_input.GetBits (ControlTable2, 11);
|
||||
|
||||
int t = m_input.GetBits (ControlTable32, 13) * 2;
|
||||
int pos = PosTable32[t] * 2;
|
||||
int alpha = PosTable32[t+1];
|
||||
int x_offset = offset_table[pos];
|
||||
int y_offset = offset_table[pos + 1];
|
||||
if (pos > 0)
|
||||
{
|
||||
offset_table[pos] = offset_table[pos-2];
|
||||
offset_table[pos+1] = offset_table[pos-1];
|
||||
offset_table[pos-2] = x_offset;
|
||||
offset_table[pos-1] = y_offset;
|
||||
}
|
||||
int src = m_rows_ptr[y_offset] + (x + x_offset) * 4;
|
||||
if (ctl >= 0xD8)
|
||||
{
|
||||
int count = ctl - 0xD6;
|
||||
Binary.CopyOverlapped (m_row_buffer, src, row_pos, count * 4);
|
||||
row_pos += count * 4;
|
||||
x += count;
|
||||
}
|
||||
else
|
||||
{
|
||||
switch (ColorCode[ctl])
|
||||
{
|
||||
case 0:
|
||||
m_row_buffer[row_pos + 2] = (byte)(m_row_buffer[src + 2] - MapRed[ctl]);
|
||||
m_row_buffer[row_pos + 1] = (byte)(m_row_buffer[src + 1] - MapGreen[ctl]);
|
||||
m_row_buffer[row_pos ] = (byte)(m_row_buffer[src ] - MapBlue[ctl]);
|
||||
break;
|
||||
case 1:
|
||||
m_row_buffer[row_pos + 2] = (byte)(m_row_buffer[src + 2] - GetDelta());
|
||||
m_row_buffer[row_pos + 1] = (byte)(m_row_buffer[src + 1] - MapGreen[ctl]);
|
||||
m_row_buffer[row_pos ] = (byte)(m_row_buffer[src ] - MapBlue[ctl]);
|
||||
break;
|
||||
case 2:
|
||||
m_row_buffer[row_pos + 2] = (byte)(m_row_buffer[src + 2] - MapRed[ctl]);
|
||||
m_row_buffer[row_pos + 1] = (byte)(m_row_buffer[src + 1] - GetDelta());
|
||||
m_row_buffer[row_pos ] = (byte)(m_row_buffer[src ] - MapBlue[ctl]);
|
||||
break;
|
||||
case 3:
|
||||
m_row_buffer[row_pos + 2] = (byte)(m_row_buffer[src + 2] - GetDelta());
|
||||
m_row_buffer[row_pos + 1] = (byte)(m_row_buffer[src + 1] - GetDelta());
|
||||
m_row_buffer[row_pos ] = (byte)(m_row_buffer[src ] - MapBlue[ctl]);
|
||||
break;
|
||||
case 4:
|
||||
m_row_buffer[row_pos + 2] = (byte)(m_row_buffer[src + 2] - MapRed[ctl]);
|
||||
m_row_buffer[row_pos + 1] = (byte)(m_row_buffer[src + 1] - MapGreen[ctl]);
|
||||
m_row_buffer[row_pos ] = (byte)(m_row_buffer[src ] - GetDelta());
|
||||
break;
|
||||
case 5:
|
||||
m_row_buffer[row_pos + 2] = (byte)(m_row_buffer[src + 2] - GetDelta());
|
||||
m_row_buffer[row_pos + 1] = (byte)(m_row_buffer[src + 1] - MapGreen[ctl]);
|
||||
m_row_buffer[row_pos ] = (byte)(m_row_buffer[src ] - GetDelta());
|
||||
break;
|
||||
case 6:
|
||||
m_row_buffer[row_pos + 2] = (byte)(m_row_buffer[src + 2] - MapRed[ctl]);
|
||||
m_row_buffer[row_pos + 1] = (byte)(m_row_buffer[src + 1] - GetDelta());
|
||||
m_row_buffer[row_pos ] = (byte)(m_row_buffer[src ] - GetDelta());
|
||||
break;
|
||||
case 7:
|
||||
m_row_buffer[row_pos + 2] = (byte)(m_row_buffer[src + 2] - GetDelta());
|
||||
m_row_buffer[row_pos + 1] = (byte)(m_row_buffer[src + 1] - GetDelta());
|
||||
m_row_buffer[row_pos ] = (byte)(m_row_buffer[src ] - GetDelta());
|
||||
break;
|
||||
}
|
||||
if (-3 == alpha)
|
||||
{
|
||||
alpha = GetDelta();
|
||||
}
|
||||
m_row_buffer[row_pos + 3] = (byte)(m_row_buffer[src + 3] - alpha);
|
||||
row_pos += 4;
|
||||
++x;
|
||||
}
|
||||
}
|
||||
Buffer.BlockCopy (m_row_buffer, m_rows_ptr[0], m_output, dst, m_stride);
|
||||
dst += m_stride;
|
||||
}
|
||||
}
|
||||
|
||||
int GetDelta ()
|
||||
{
|
||||
int d = m_input.GetBits (DeltaTable1, 8);
|
||||
if (0x2B == d)
|
||||
d += m_input.GetBits (DeltaTable2, 13);
|
||||
return DeltaTable3[d];
|
||||
}
|
||||
|
||||
static byte[] LoadResource (string name) => EmbeddedResource.Load (name, typeof(ImgReader));
|
||||
|
||||
#region IDisposable Members
|
||||
bool _disposed = false;
|
||||
public void Dispose ()
|
||||
{
|
||||
if (!_disposed)
|
||||
{
|
||||
m_input.Dispose();
|
||||
_disposed = true;
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Bitmap Tables
|
||||
static readonly Lazy<byte[]> s_control_table1 = new Lazy<byte[]> (() => LoadResource ("ImgControlTable1"));
|
||||
static readonly Lazy<byte[]> s_control_table2 = new Lazy<byte[]> (() => LoadResource ("ImgControlTable2"));
|
||||
static readonly Lazy<byte[]> s_control_table32 = new Lazy<byte[]> (() => LoadResource ("ImgControlTable32"));
|
||||
static readonly Lazy<byte[]> s_delta_table2 = new Lazy<byte[]> (() => LoadResource ("ImgDeltaTable2"));
|
||||
|
||||
static byte[] ControlTable1 { get { return s_control_table1.Value; } }
|
||||
static byte[] ControlTable2 { get { return s_control_table2.Value; } }
|
||||
static byte[] ControlTable32 { get { return s_control_table32.Value; } }
|
||||
static byte[] DeltaTable2 { get { return s_delta_table2.Value; } }
|
||||
|
||||
static readonly byte[] PosTable24 = {
|
||||
4, 2, 1, 0, 4, 3, 1, 0, 3, 1, 1, 0, 4, 4, 1, 0, 5, 5, 1, 0, 5, 7, 1, 0, 3, 1, 1, 0, 5, 9, 1, 0, 4,
|
||||
2, 1, 0, 4, 3, 1, 0, 3, 1, 1, 0, 4, 4, 1, 0, 5, 6, 1, 0, 5, 8, 1, 0, 3, 1, 1, 0, 5, 10, 1, 0,
|
||||
};
|
||||
static readonly sbyte[] PosTable32 = {
|
||||
0, 0, 1, 0, 2, 0, 3, 0, 4, 0, 5, 0, 6, 0, 7, 0, 8, 0, 9, 0, 10, 0, 0, -3, 1, -3, 2, -3, 3, -3, 4,
|
||||
-3, 5, -3, 6, -3, 7, -3, 8, -3, 9, -3, 10, -3, 0, -1, 1, -1, 2, -1, 3, -1, 4, -1, 5, -1, 6, -1, 7,
|
||||
-1, 8, -1, 9, -1, 10, -1, 0, 1, 1, 1, 2, 1, 3, 1, 4, 1, 5, 1, 6, 1, 7, 1, 8, 1, 9, 1, 10, 1, 0,
|
||||
-2, 1, -2, 2, -2, 3, -2, 4, -2, 5, -2, 6, -2, 7, -2, 8, -2, 9, -2, 10, -2, 0, 2, 1, 2, 2, 2, 3, 2,
|
||||
4, 2, 5, 2, 6, 2, 7, 2, 8, 2, 9, 2, 10, 2,
|
||||
};
|
||||
static readonly byte[] ColorCode = {
|
||||
7, 3, 3, 3, 3, 3, 5, 1, 1, 1, 1, 1, 5, 1, 1, 1, 1, 1, 5, 1, 1, 1, 1, 1,
|
||||
5, 1, 1, 1, 1, 1, 5, 1, 1, 1, 1, 1, 6, 2, 2, 2, 2, 2, 4, 0, 0, 0, 0, 0,
|
||||
4, 0, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0,
|
||||
6, 2, 2, 2, 2, 2, 4, 0, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0,
|
||||
4, 0, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, 6, 2, 2, 2, 2, 2, 4, 0, 0, 0, 0, 0,
|
||||
4, 0, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0,
|
||||
6, 2, 2, 2, 2, 2, 4, 0, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0,
|
||||
4, 0, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, 6, 2, 2, 2, 2, 2, 4, 0, 0, 0, 0, 0,
|
||||
4, 0, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0,
|
||||
};
|
||||
|
||||
static readonly byte[] DeltaTable1 = {
|
||||
7, 0x13, 3, 0, 6, 0x0D, 4, 4, 5, 7, 3, 1, 5, 8, 4, 5, 4, 2, 3, 0, 4, 0x2B, 6, 0x10, 4, 3, 3, 1, 5,
|
||||
9, 5, 0x0B, 5, 6, 3, 0, 6, 0x0F, 4, 4, 6, 0x0C, 3, 1, 8, 0x23, 4, 5, 4, 2, 3, 0, 4, 0x2B, 6, 0x11,
|
||||
4, 3, 3, 1, 5, 0x0A, 8, 0x27, 7, 0x15, 3, 0, 6, 0x0E, 4, 4, 5, 7, 3, 1, 5, 8, 4, 5, 4, 2, 3, 0, 4,
|
||||
0x2B, 7, 0x1B, 4, 3, 3, 1, 5, 9, 5, 0x0B, 5, 6, 3, 0, 7, 0x18, 4, 4, 8, 0x22, 3, 1, 7, 0x19, 4, 5,
|
||||
4, 2, 3, 0, 4, 0x2B, 6, 0x12, 4, 3, 3, 1, 5, 0x0A, 6, 0x14, 8, 0x1D, 3, 0, 6, 0x0D, 4, 4, 5, 7, 3,
|
||||
1, 5, 8, 4, 5, 4, 2, 3, 0, 4, 0x2B, 6, 0x10, 4, 3, 3, 1, 5, 9, 5, 0x0B, 5, 6, 3, 0, 6, 0x0F, 4, 4,
|
||||
6, 0x0C, 3, 1, 7, 0x1A, 4, 5, 4, 2, 3, 0, 4, 0x2B, 6, 0x11, 4, 3, 3, 1, 5, 0x0A, 7, 0x1C, 8, 0x20,
|
||||
3, 0, 6, 0x0E, 4, 4, 5, 7, 3, 1, 5, 8, 4, 5, 4, 2, 3, 0, 4, 0x2B, 8, 0x28, 4, 3, 3, 1, 5, 9, 5,
|
||||
0x0B, 5, 6, 3, 0, 7, 0x17, 4, 4, 7, 0x16, 3, 1, 8, 0x26, 4, 5, 4, 2, 3, 0, 4, 0x2B, 6, 0x12, 4, 3,
|
||||
3, 1, 5, 0x0A, 6, 0x14, 7, 0x13, 3, 0, 6, 0x0D, 4, 4, 5, 7, 3, 1, 5, 8, 4, 5, 4, 2, 3, 0, 4, 0x2B,
|
||||
6, 0x10, 4, 3, 3, 1, 5, 9, 5, 0x0B, 5, 6, 3, 0, 6, 0x0F, 4, 4, 6, 0x0C, 3, 1, 8, 0x24, 4, 5, 4, 2,
|
||||
3, 0, 4, 0x2B, 6, 0x11, 4, 3, 3, 1, 5, 0x0A, 8, 0x29, 7, 0x15, 3, 0, 6, 0x0E, 4, 4, 5, 7, 3, 1, 5,
|
||||
8, 4, 5, 4, 2, 3, 0, 4, 0x2B, 7, 0x1B, 4, 3, 3, 1, 5, 9, 5, 0x0B, 5, 6, 3, 0, 7, 0x18, 4, 4, 8,
|
||||
0x21, 3, 1, 7, 0x19, 4, 5, 4, 2, 3, 0, 4, 0x2B, 6, 0x12, 4, 3, 3, 1, 5, 0x0A, 6, 0x14, 8, 0x1E, 3,
|
||||
0, 6, 0x0D, 4, 4, 5, 7, 3, 1, 5, 8, 4, 5, 4, 2, 3, 0, 4, 0x2B, 6, 0x10, 4, 3, 3, 1, 5, 9, 5, 0x0B,
|
||||
5, 6, 3, 0, 6, 0x0F, 4, 4, 6, 0x0C, 3, 1, 7, 0x1A, 4, 5, 4, 2, 3, 0, 4, 0x2B, 6, 0x11, 4, 3, 3, 1,
|
||||
5, 0x0A, 7, 0x1C, 8, 0x1F, 3, 0, 6, 0x0E, 4, 4, 5, 7, 3, 1, 5, 8, 4, 5, 4, 2, 3, 0, 4, 0x2B, 8,
|
||||
0x2A, 4, 3, 3, 1, 5, 9, 5, 0x0B, 5, 6, 3, 0, 7, 0x17, 4, 4, 7, 0x16, 3, 1, 8, 0x25, 4, 5, 4, 2, 3,
|
||||
0, 4, 0x2B, 6, 0x12, 4, 3, 3, 1, 5, 0x0A, 6, 0x14,
|
||||
};
|
||||
static readonly byte[] DeltaTable3 = {
|
||||
0x03, 0xFD, 0x04, 0xFC, 0x05, 0xFB, 0x06, 0xFA, 0x07, 0xF9, 0x08, 0xF8, 0x09, 0xF7, 0x0A, 0xF6,
|
||||
0x0B, 0xF5, 0x0C, 0xF4, 0x0D, 0xF3, 0x0E, 0xF2, 0x0F, 0xF1, 0x10, 0xF0, 0x11, 0xEF, 0x12, 0xEE,
|
||||
0x13, 0xED, 0x14, 0xEC, 0x15, 0xEB, 0x16, 0xEA, 0x17, 0xE9, 0x18, 0xE8, 0x19, 0xE7, 0x1A, 0xE6,
|
||||
0x1B, 0xE5, 0x1C, 0xE4, 0x1D, 0xE3, 0x1E, 0xE2, 0x1F, 0xE1, 0x20, 0xE0, 0x21, 0xDF, 0x22, 0xDE,
|
||||
0x23, 0xDD, 0x24, 0xDC, 0x25, 0xDB, 0x26, 0xDA, 0x27, 0xD9, 0x28, 0xD8, 0x29, 0xD7, 0x2A, 0xD6,
|
||||
0x2B, 0xD5, 0x2C, 0xD4, 0x2D, 0xD3, 0x2E, 0xD2, 0x2F, 0xD1, 0x30, 0xD0, 0x31, 0xCF, 0x32, 0xCE,
|
||||
0x33, 0xCD, 0x34, 0xCC, 0x35, 0xCB, 0x36, 0xCA, 0x37, 0xC9, 0x38, 0xC8, 0x39, 0xC7, 0x3A, 0xC6,
|
||||
0x3B, 0xC5, 0x3C, 0xC4, 0x3D, 0xC3, 0x3E, 0xC2, 0x3F, 0xC1, 0x40, 0xC0, 0x41, 0xBF, 0x42, 0xBE,
|
||||
0x43, 0xBD, 0x44, 0xBC, 0x45, 0xBB, 0x46, 0xBA, 0x47, 0xB9, 0x48, 0xB8, 0x49, 0xB7, 0x4A, 0xB6,
|
||||
0x4B, 0xB5, 0x4C, 0xB4, 0x4D, 0xB3, 0x4E, 0xB2, 0x4F, 0xB1, 0x50, 0xB0, 0x51, 0xAF, 0x52, 0xAE,
|
||||
0x53, 0xAD, 0x54, 0xAC, 0x55, 0xAB, 0x56, 0xAA, 0x57, 0xA9, 0x58, 0xA8, 0x59, 0xA7, 0x5A, 0xA6,
|
||||
0x5B, 0xA5, 0x5C, 0xA4, 0x5D, 0xA3, 0x5E, 0xA2, 0x5F, 0xA1, 0x60, 0xA0, 0x61, 0x9F, 0x62, 0x9E,
|
||||
0x63, 0x9D, 0x64, 0x9C, 0x65, 0x9B, 0x66, 0x9A, 0x67, 0x99, 0x68, 0x98, 0x69, 0x97, 0x6A, 0x96,
|
||||
0x6B, 0x95, 0x6C, 0x94, 0x6D, 0x93, 0x6E, 0x92, 0x6F, 0x91, 0x70, 0x90, 0x71, 0x8F, 0x72, 0x8E,
|
||||
0x73, 0x8D, 0x74, 0x8C, 0x75, 0x8B, 0x76, 0x8A, 0x77, 0x89, 0x78, 0x88, 0x79, 0x87, 0x7A, 0x86,
|
||||
0x7B, 0x85, 0x7C, 0x84, 0x7D, 0x83, 0x7E, 0x82, 0x7F, 0x81, 0x80, 0,
|
||||
};
|
||||
static readonly byte[] MapRed = {
|
||||
0xFD, 0xFD, 0xFD, 0xFD, 0xFD, 0xFD, 0xFD, 0xFD, 0xFD, 0xFD, 0xFD, 0xFD, 0xFD, 0xFD, 0xFD, 0xFD,
|
||||
0xFD, 0xFD, 0xFD, 0xFD, 0xFD, 0xFD, 0xFD, 0xFD, 0xFD, 0xFD, 0xFD, 0xFD, 0xFD, 0xFD, 0xFD, 0xFD,
|
||||
0xFD, 0xFD, 0xFD, 0xFD, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE,
|
||||
0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE,
|
||||
0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2,
|
||||
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
|
||||
};
|
||||
static readonly byte[] MapGreen = {
|
||||
0x0FD, 0x0FD, 0x0FD, 0x0FD, 0x0FD, 0x0FD, 0x0FE, 0x0FE, 0x0FE, 0x0FE, 0x0FE, 0x0FE, 0x0FF, 0x0FF,
|
||||
0x0FF, 0x0FF, 0x0FF, 0x0FF, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 0x0FD, 0x0FD,
|
||||
0x0FD, 0x0FD, 0x0FD, 0x0FD, 0x0FE, 0x0FE, 0x0FE, 0x0FE, 0x0FE, 0x0FE, 0x0FF, 0x0FF, 0x0FF, 0x0FF,
|
||||
0x0FF, 0x0FF, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 0x0FD, 0x0FD, 0x0FD, 0x0FD,
|
||||
0x0FD, 0x0FD, 0x0FE, 0x0FE, 0x0FE, 0x0FE, 0x0FE, 0x0FE, 0x0FF, 0x0FF, 0x0FF, 0x0FF, 0x0FF, 0x0FF,
|
||||
0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 0x0FD, 0x0FD, 0x0FD, 0x0FD, 0x0FD, 0x0FD,
|
||||
0x0FE, 0x0FE, 0x0FE, 0x0FE, 0x0FE, 0x0FE, 0x0FF, 0x0FF, 0x0FF, 0x0FF, 0x0FF, 0x0FF, 0, 0, 0, 0, 0,
|
||||
0, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 0x0FD, 0x0FD, 0x0FD, 0x0FD, 0x0FD, 0x0FD, 0x0FE, 0x0FE,
|
||||
0x0FE, 0x0FE, 0x0FE, 0x0FE, 0x0FF, 0x0FF, 0x0FF, 0x0FF, 0x0FF, 0x0FF, 0, 0, 0, 0, 0, 0, 1, 1, 1,
|
||||
1, 1, 1, 2, 2, 2, 2, 2, 2, 0x0FD, 0x0FD, 0x0FD, 0x0FD, 0x0FD, 0x0FD, 0x0FE, 0x0FE, 0x0FE, 0x0FE,
|
||||
0x0FE, 0x0FE, 0x0FF, 0x0FF, 0x0FF, 0x0FF, 0x0FF, 0x0FF, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 2, 2,
|
||||
2, 2, 2, 2,
|
||||
};
|
||||
static readonly byte[] MapBlue = {
|
||||
0x0FD, 0x0FE, 0x0FF, 0, 1, 2, 0x0FD, 0x0FE, 0x0FF, 0, 1, 2,
|
||||
0x0FD, 0x0FE, 0x0FF, 0, 1, 2, 0x0FD, 0x0FE, 0x0FF, 0, 1, 2,
|
||||
0x0FD, 0x0FE, 0x0FF, 0, 1, 2, 0x0FD, 0x0FE, 0x0FF, 0, 1, 2,
|
||||
0x0FD, 0x0FE, 0x0FF, 0, 1, 2, 0x0FD, 0x0FE, 0x0FF, 0, 1, 2,
|
||||
0x0FD, 0x0FE, 0x0FF, 0, 1, 2, 0x0FD, 0x0FE, 0x0FF, 0, 1, 2,
|
||||
0x0FD, 0x0FE, 0x0FF, 0, 1, 2, 0x0FD, 0x0FE, 0x0FF, 0, 1, 2,
|
||||
0x0FD, 0x0FE, 0x0FF, 0, 1, 2, 0x0FD, 0x0FE, 0x0FF, 0, 1, 2,
|
||||
0x0FD, 0x0FE, 0x0FF, 0, 1, 2, 0x0FD, 0x0FE, 0x0FF, 0, 1, 2,
|
||||
0x0FD, 0x0FE, 0x0FF, 0, 1, 2, 0x0FD, 0x0FE, 0x0FF, 0, 1, 2,
|
||||
0x0FD, 0x0FE, 0x0FF, 0, 1, 2, 0x0FD, 0x0FE, 0x0FF, 0, 1, 2,
|
||||
0x0FD, 0x0FE, 0x0FF, 0, 1, 2, 0x0FD, 0x0FE, 0x0FF, 0, 1, 2,
|
||||
0x0FD, 0x0FE, 0x0FF, 0, 1, 2, 0x0FD, 0x0FE, 0x0FF, 0, 1, 2,
|
||||
0x0FD, 0x0FE, 0x0FF, 0, 1, 2, 0x0FD, 0x0FE, 0x0FF, 0, 1, 2,
|
||||
0x0FD, 0x0FE, 0x0FF, 0, 1, 2, 0x0FD, 0x0FE, 0x0FF, 0, 1, 2,
|
||||
0x0FD, 0x0FE, 0x0FF, 0, 1, 2, 0x0FD, 0x0FE, 0x0FF, 0, 1, 2,
|
||||
0x0FD, 0x0FE, 0x0FF, 0, 1, 2, 0x0FD, 0x0FE, 0x0FF, 0, 1, 2,
|
||||
0x0FD, 0x0FE, 0x0FF, 0, 1, 2, 0x0FD, 0x0FE, 0x0FF, 0, 1, 2,
|
||||
0x0FD, 0x0FE, 0x0FF, 0, 1, 2, 0x0FD, 0x0FE, 0x0FF, 0, 1, 2,
|
||||
};
|
||||
#endregion
|
||||
}
|
||||
|
||||
internal class ImgBitStream : BitStream
|
||||
{
|
||||
public ImgBitStream (Stream file, bool leave_open = false) : base (file, leave_open)
|
||||
{
|
||||
}
|
||||
|
||||
public int GetBits (byte[] table, int count)
|
||||
{
|
||||
int n = PeekBits (count) * 2;
|
||||
count = table[n];
|
||||
m_bits >>= count;
|
||||
m_cached_bits -= count;
|
||||
return table[n + 1];
|
||||
}
|
||||
|
||||
public int PeekBits (int count)
|
||||
{
|
||||
if (m_cached_bits < count)
|
||||
{
|
||||
int b = m_input.ReadByte();
|
||||
if (-1 == b)
|
||||
b = 0; // throw new EndOfStreamException();
|
||||
m_bits |= ByteMap[b] << m_cached_bits;
|
||||
m_cached_bits += 8;
|
||||
if (m_cached_bits < count)
|
||||
{
|
||||
b = m_input.ReadByte();
|
||||
if (-1 == b)
|
||||
b = 0; // throw new EndOfStreamException();
|
||||
m_bits |= ByteMap[b] << m_cached_bits;
|
||||
m_cached_bits += 8;
|
||||
}
|
||||
}
|
||||
return m_bits & g_bitMask[count];
|
||||
}
|
||||
|
||||
static readonly int[] g_bitMask = {
|
||||
0x00000000, 0x00000001, 0x00000003, 0x00000007, 0x0000000F, 0x0000001F, 0x0000003F, 0x0000007F,
|
||||
0x000000FF, 0x000001FF, 0x000003FF, 0x000007FF, 0x00000FFF, 0x00001FFF, 0x00003FFF, 0x00007FFF,
|
||||
0x0000FFFF, 0x0001FFFF, 0x0003FFFF, 0x0007FFFF, 0x000FFFFF, 0x001FFFFF, 0x003FFFFF, 0x007FFFFF,
|
||||
0x00FFFFFF, 0x01FFFFFF, 0x03FFFFFF, 0x07FFFFFF, 0x0FFFFFFF, 0x1FFFFFFF, 0x3FFFFFFF, 0x7FFFFFFF,
|
||||
};
|
||||
|
||||
internal static readonly byte[] ByteMap = {
|
||||
0x00, 0x80, 0x40, 0xC0, 0x20, 0xA0, 0x60, 0xE0, 0x10, 0x90, 0x50, 0xD0, 0x30, 0xB0, 0x70, 0xF0,
|
||||
0x08, 0x88, 0x48, 0xC8, 0x28, 0xA8, 0x68, 0xE8, 0x18, 0x98, 0x58, 0xD8, 0x38, 0xB8, 0x78, 0xF8,
|
||||
0x04, 0x84, 0x44, 0xC4, 0x24, 0xA4, 0x64, 0xE4, 0x14, 0x94, 0x54, 0xD4, 0x34, 0xB4, 0x74, 0xF4,
|
||||
0x0C, 0x8C, 0x4C, 0xCC, 0x2C, 0xAC, 0x6C, 0xEC, 0x1C, 0x9C, 0x5C, 0xDC, 0x3C, 0xBC, 0x7C, 0xFC,
|
||||
0x02, 0x82, 0x42, 0xC2, 0x22, 0xA2, 0x62, 0xE2, 0x12, 0x92, 0x52, 0xD2, 0x32, 0xB2, 0x72, 0xF2,
|
||||
0x0A, 0x8A, 0x4A, 0xCA, 0x2A, 0xAA, 0x6A, 0xEA, 0x1A, 0x9A, 0x5A, 0xDA, 0x3A, 0xBA, 0x7A, 0xFA,
|
||||
0x06, 0x86, 0x46, 0xC6, 0x26, 0xA6, 0x66, 0xE6, 0x16, 0x96, 0x56, 0xD6, 0x36, 0xB6, 0x76, 0xF6,
|
||||
0x0E, 0x8E, 0x4E, 0xCE, 0x2E, 0xAE, 0x6E, 0xEE, 0x1E, 0x9E, 0x5E, 0xDE, 0x3E, 0xBE, 0x7E, 0xFE,
|
||||
0x01, 0x81, 0x41, 0xC1, 0x21, 0xA1, 0x61, 0xE1, 0x11, 0x91, 0x51, 0xD1, 0x31, 0xB1, 0x71, 0xF1,
|
||||
0x09, 0x89, 0x49, 0xC9, 0x29, 0xA9, 0x69, 0xE9, 0x19, 0x99, 0x59, 0xD9, 0x39, 0xB9, 0x79, 0xF9,
|
||||
0x05, 0x85, 0x45, 0xC5, 0x25, 0xA5, 0x65, 0xE5, 0x15, 0x95, 0x55, 0xD5, 0x35, 0xB5, 0x75, 0xF5,
|
||||
0x0D, 0x8D, 0x4D, 0xCD, 0x2D, 0xAD, 0x6D, 0xED, 0x1D, 0x9D, 0x5D, 0xDD, 0x3D, 0xBD, 0x7D, 0xFD,
|
||||
0x03, 0x83, 0x43, 0xC3, 0x23, 0xA3, 0x63, 0xE3, 0x13, 0x93, 0x53, 0xD3, 0x33, 0xB3, 0x73, 0xF3,
|
||||
0x0B, 0x8B, 0x4B, 0xCB, 0x2B, 0xAB, 0x6B, 0xEB, 0x1B, 0x9B, 0x5B, 0xDB, 0x3B, 0xBB, 0x7B, 0xFB,
|
||||
0x07, 0x87, 0x47, 0xC7, 0x27, 0xA7, 0x67, 0xE7, 0x17, 0x97, 0x57, 0xD7, 0x37, 0xB7, 0x77, 0xF7,
|
||||
0x0F, 0x8F, 0x4F, 0xCF, 0x2F, 0xAF, 0x6F, 0xEF, 0x1F, 0x9F, 0x5F, 0xDF, 0x3F, 0xBF, 0x7F, 0xFF,
|
||||
};
|
||||
}
|
||||
}
|
BIN
ArcFormats/ScrPlayer/ImgControlTable1
Normal file
BIN
ArcFormats/ScrPlayer/ImgControlTable1
Normal file
Binary file not shown.
BIN
ArcFormats/ScrPlayer/ImgControlTable2
Normal file
BIN
ArcFormats/ScrPlayer/ImgControlTable2
Normal file
Binary file not shown.
BIN
ArcFormats/ScrPlayer/ImgControlTable32
Normal file
BIN
ArcFormats/ScrPlayer/ImgControlTable32
Normal file
Binary file not shown.
BIN
ArcFormats/ScrPlayer/ImgDeltaTable2
Normal file
BIN
ArcFormats/ScrPlayer/ImgDeltaTable2
Normal file
Binary file not shown.
63
ArcFormats/Software House Parsley/ArcScn.cs
Normal file
63
ArcFormats/Software House Parsley/ArcScn.cs
Normal file
@ -0,0 +1,63 @@
|
||||
//! \file ArcScn.cs
|
||||
//! \date 2023 Aug 28
|
||||
//! \brief Software House Parsley scripts archive.
|
||||
//
|
||||
// Copyright (C) 2023 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.Collections.Generic;
|
||||
using System.ComponentModel.Composition;
|
||||
|
||||
namespace GameRes.Formats.Parsley
|
||||
{
|
||||
[Export(typeof(ArchiveFormat))]
|
||||
public class ScnDatOpener : ArchiveFormat
|
||||
{
|
||||
public override string Tag { get => "DAT/SCN"; }
|
||||
public override string Description { get => "Software House Parsley scenario archive"; }
|
||||
public override uint Signature { get => 0; }
|
||||
public override bool IsHierarchic { get => false; }
|
||||
public override bool CanWrite { get => false; }
|
||||
|
||||
public override ArcFile TryOpen (ArcView file)
|
||||
{
|
||||
if (!VFS.IsPathEqualsToFileName (file.Name, "scn.dat"))
|
||||
return null;
|
||||
uint base_offset = 0x1400;
|
||||
uint index_pos = 0;
|
||||
var dir = new List<Entry>();
|
||||
while (index_pos < base_offset && file.View.ReadByte (index_pos) != 0)
|
||||
{
|
||||
var name = file.View.ReadString (index_pos, 0x20);
|
||||
var entry = Create<Entry> (name);
|
||||
entry.Offset = file.View.ReadUInt32 (index_pos+0x20) + base_offset;
|
||||
entry.Size = file.View.ReadUInt32 (index_pos+0x24);
|
||||
if (!entry.CheckPlacement (file.MaxOffset))
|
||||
return null;
|
||||
dir.Add (entry);
|
||||
index_pos += 0x28;
|
||||
}
|
||||
if (dir.Count == 0)
|
||||
return null;
|
||||
return new ArcFile (file, this, dir);
|
||||
}
|
||||
}
|
||||
}
|
87
ArcFormats/Unity/ScriptDSM.cs
Normal file
87
ArcFormats/Unity/ScriptDSM.cs
Normal file
@ -0,0 +1,87 @@
|
||||
//! \file ScriptDSM.cs
|
||||
//! \date 2023 Sep 03
|
||||
//! \brief Decrypt data.dsm script.
|
||||
//
|
||||
// Copyright (C) 2023 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.Security.Cryptography;
|
||||
using System.Text;
|
||||
|
||||
namespace GameRes.Formats.NScripter
|
||||
{
|
||||
[Export(typeof(ScriptFormat))]
|
||||
public class DsmConverter : GenericScriptFormat
|
||||
{
|
||||
public override string Tag { get => "DSM/UTAGE"; }
|
||||
public override string Description { get => "UTAGE Unity engine script file"; }
|
||||
public override uint Signature { get => 0; }
|
||||
|
||||
public override bool IsScript (IBinaryStream file)
|
||||
{
|
||||
return VFS.IsPathEqualsToFileName (file.Name, "data.dsm");
|
||||
}
|
||||
|
||||
public override Stream ConvertFrom (IBinaryStream file)
|
||||
{
|
||||
using (var reader = new StreamReader (file.AsStream))
|
||||
{
|
||||
var sourceString = reader.ReadToEnd();
|
||||
var text = DsmDecryptor.DecryptString (sourceString, "pass");
|
||||
return new BinMemoryStream (text, file.Name);
|
||||
}
|
||||
}
|
||||
|
||||
public override Stream ConvertBack (IBinaryStream file)
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
}
|
||||
|
||||
internal static class DsmDecryptor
|
||||
{
|
||||
internal static void GenerateKeyFromPassword (string password, int keySize, out byte[] key, int blockSize, out byte[] iv)
|
||||
{
|
||||
var bytes = Encoding.UTF8.GetBytes ("saltは必ず8バイト以上");
|
||||
var derive = new Rfc2898DeriveBytes (password, bytes);
|
||||
derive.IterationCount = 1000;
|
||||
key = derive.GetBytes (keySize / 8);
|
||||
iv = derive.GetBytes (blockSize / 8);
|
||||
}
|
||||
|
||||
internal static byte[] DecryptString (string sourceString, string password)
|
||||
{
|
||||
var rij = new RijndaelManaged();
|
||||
byte[] key, iv;
|
||||
GenerateKeyFromPassword (password, rij.KeySize, out key, rij.BlockSize, out iv);
|
||||
rij.Key = key;
|
||||
rij.IV = iv;
|
||||
var array = Convert.FromBase64String (sourceString);
|
||||
using (var cryptoTransform = rij.CreateDecryptor())
|
||||
{
|
||||
return cryptoTransform.TransformFinalBlock (array, 0, array.Length);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -150,6 +150,8 @@
|
||||
<Compile Include="Cabinet\ArcCAB.cs" />
|
||||
<Compile Include="CellWorks\ArcDB.cs" />
|
||||
<Compile Include="Artemis\GplexBuffers.cs" />
|
||||
<Compile Include="Microsoft\ArcEXE.cs" />
|
||||
<Compile Include="Microsoft\ArcNE.cs" />
|
||||
<Compile Include="Opus\AudioOPUS.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
<Compile Include="RPGMaker\ArcRGSS.cs" />
|
||||
|
184
Experimental/Microsoft/ArcEXE.cs
Normal file
184
Experimental/Microsoft/ArcEXE.cs
Normal file
@ -0,0 +1,184 @@
|
||||
//! \file ArcEXE.cs
|
||||
//! \date 2023 Aug 24
|
||||
//! \brief Access portable executable (PE) resources.
|
||||
//
|
||||
// Copyright (C) 2023 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.Collections.Generic;
|
||||
using System.ComponentModel.Composition;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
|
||||
namespace GameRes.Formats.Microsoft
|
||||
{
|
||||
[Export(typeof(ArchiveFormat))]
|
||||
public class ExeOpener : ArchiveFormat
|
||||
{
|
||||
public override string Tag { get => "EXE"; }
|
||||
public override string Description { get => "Windows executable resources"; }
|
||||
public override uint Signature { get => 0; }
|
||||
public override bool IsHierarchic { get => true; }
|
||||
public override bool CanWrite { get => false; }
|
||||
|
||||
public ExeOpener ()
|
||||
{
|
||||
Extensions = new[] { "exe",/* "dll"*/ };
|
||||
}
|
||||
|
||||
static readonly Dictionary<string, string> RuntimeTypeMap = new Dictionary<string, string>() {
|
||||
{ "#2", "RT_BITMAP" },
|
||||
{ "#10", "RT_RCDATA" },
|
||||
};
|
||||
|
||||
static readonly Dictionary<string, string> ExtensionTypeMap = new Dictionary<string, string>() {
|
||||
{ "PNG", ".PNG" },
|
||||
{ "WAVE", ".WAV" },
|
||||
{ "MIDS", ".MID" },
|
||||
{ "SCR", ".BIN" },
|
||||
{ "#2", ".BMP" },
|
||||
{ "#10", ".BIN" },
|
||||
};
|
||||
|
||||
public override ArcFile TryOpen (ArcView file)
|
||||
{
|
||||
if (!file.View.AsciiEqual (0, "MZ") || VFS.IsVirtual)
|
||||
return null;
|
||||
var res = new ExeFile.ResourceAccessor (file.Name);
|
||||
try
|
||||
{
|
||||
var dir = new List<Entry>();
|
||||
foreach (var type in res.EnumTypes())
|
||||
{
|
||||
string dir_name = type;
|
||||
if (type.StartsWith ("#") && !RuntimeTypeMap.TryGetValue (type, out dir_name))
|
||||
continue;
|
||||
string ext;
|
||||
if (!ExtensionTypeMap.TryGetValue (type, out ext))
|
||||
ext = "";
|
||||
foreach (var name in res.EnumNames (type))
|
||||
{
|
||||
string full_name = name;
|
||||
if (name.StartsWith ("#"))
|
||||
full_name = IdToString (name);
|
||||
full_name = string.Join ("/", dir_name, full_name) + ext;
|
||||
var entry = Create<ResourceEntry> (full_name);
|
||||
entry.NativeName = name;
|
||||
entry.NativeType = type;
|
||||
entry.Offset = 0;
|
||||
entry.Size = res.GetResourceSize (name, type);
|
||||
dir.Add (entry);
|
||||
}
|
||||
}
|
||||
if (0 == dir.Count)
|
||||
{
|
||||
res.Dispose();
|
||||
return null;
|
||||
}
|
||||
return new ResourcesArchive (file, this, dir, res);
|
||||
}
|
||||
catch
|
||||
{
|
||||
res.Dispose();
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
public override Stream OpenEntry (ArcFile arc, Entry entry)
|
||||
{
|
||||
var rarc = (ResourcesArchive)arc;
|
||||
var rent = (ResourceEntry)entry;
|
||||
var data = rarc.Accessor.GetResource (rent.NativeName, rent.NativeType);
|
||||
if (null == data)
|
||||
return Stream.Null;
|
||||
return new BinMemoryStream (data, rent.Name);
|
||||
}
|
||||
|
||||
public override IImageDecoder OpenImage (ArcFile arc, Entry entry)
|
||||
{
|
||||
var rent = (ResourceEntry)entry;
|
||||
if (rent.NativeType != "#2")
|
||||
return base.OpenImage (arc, entry);
|
||||
var rarc = (ResourcesArchive)arc;
|
||||
var bitmap = new byte[14 + entry.Size];
|
||||
int length = rarc.Accessor.ReadResource (rent.NativeName, rent.NativeType, bitmap, 14);
|
||||
|
||||
length += 14;
|
||||
bitmap[0] = (byte)'B';
|
||||
bitmap[1] = (byte)'M';
|
||||
LittleEndian.Pack (length, bitmap, 2);
|
||||
int bits_length = bitmap.ToInt32 (0x22);
|
||||
int bits_pos = length - bits_length;
|
||||
if (bits_length == 0)
|
||||
bits_pos = bitmap.ToInt32 (14) + 22;
|
||||
LittleEndian.Pack (bits_pos, bitmap, 10);
|
||||
|
||||
var bm = new BinMemoryStream (bitmap, 0, length, entry.Name);
|
||||
var info = ImageFormat.Bmp.ReadMetaData (bm);
|
||||
if (null == info)
|
||||
{
|
||||
bm.Dispose();
|
||||
throw new InvalidFormatException ("Invalid bitmap resource.");
|
||||
}
|
||||
bm.Position = 0;
|
||||
return new ImageFormatDecoder (bm, ImageFormat.Bmp, info);
|
||||
}
|
||||
|
||||
internal static string IdToString (string id)
|
||||
{
|
||||
if (id.Length > 1 && id[0] == '#' && char.IsDigit (id[1]))
|
||||
id = id.Substring (1).PadLeft (5, '0');
|
||||
return id;
|
||||
}
|
||||
}
|
||||
|
||||
internal class ResourceEntry : Entry
|
||||
{
|
||||
public string NativeName;
|
||||
public string NativeType;
|
||||
}
|
||||
|
||||
internal class ResourcesArchive : ArcFile
|
||||
{
|
||||
public readonly ExeFile.ResourceAccessor Accessor;
|
||||
|
||||
public ResourcesArchive (ArcView arc, ArchiveFormat impl, ICollection<Entry> dir, ExeFile.ResourceAccessor acc)
|
||||
: base (arc, impl, dir)
|
||||
{
|
||||
Accessor = acc;
|
||||
}
|
||||
|
||||
#region IDisposable Members
|
||||
bool _acc_disposed = false;
|
||||
protected override void Dispose (bool disposing)
|
||||
{
|
||||
if (_acc_disposed)
|
||||
return;
|
||||
if (disposing)
|
||||
Accessor.Dispose();
|
||||
_acc_disposed = true;
|
||||
base.Dispose (disposing);
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
103
Experimental/Microsoft/ArcNE.cs
Normal file
103
Experimental/Microsoft/ArcNE.cs
Normal file
@ -0,0 +1,103 @@
|
||||
//! \file ArcNE.cs
|
||||
//! \date 2023 Aug 29
|
||||
//! \brief Access 16-bit NE executable resources.
|
||||
//
|
||||
// Copyright (C) 2023 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;
|
||||
|
||||
namespace GameRes.Formats.Microsoft
|
||||
{
|
||||
[Export(typeof(ArchiveFormat))]
|
||||
public class PakOpener : ArchiveFormat
|
||||
{
|
||||
public override string Tag { get => "EXE/NE"; }
|
||||
public override string Description { get => "Windows 16-bit executable resources"; }
|
||||
public override uint Signature { get => 0; }
|
||||
public override bool IsHierarchic { get => true; }
|
||||
public override bool CanWrite { get => false; }
|
||||
|
||||
static readonly Dictionary<int, string> TypeMap = new Dictionary<int, string> {
|
||||
{ 1, "RT_CURSOR" },
|
||||
{ 2, "RT_BITMAP" },
|
||||
{ 3, "RT_ICON" },
|
||||
{ 4, "RT_MENU" },
|
||||
{ 5, "RT_DIALOG" },
|
||||
{ 6, "RT_STRING" },
|
||||
{ 10, "RT_DATA" },
|
||||
{ 11, "RT_MESSAGETABLE" },
|
||||
{ 16, "RT_VERSION" },
|
||||
};
|
||||
|
||||
public override ArcFile TryOpen (ArcView file)
|
||||
{
|
||||
if (!file.View.AsciiEqual (0, "MZ"))
|
||||
return null;
|
||||
uint ne_offset = file.View.ReadUInt32 (0x3C);
|
||||
if (!file.View.AsciiEqual (ne_offset, "NE"))
|
||||
return null;
|
||||
uint res_table_offset = file.View.ReadUInt16 (ne_offset+0x24) + ne_offset;
|
||||
if (res_table_offset <= ne_offset || res_table_offset >= file.MaxOffset)
|
||||
return null;
|
||||
int shift = file.View.ReadUInt16 (res_table_offset);
|
||||
res_table_offset += 2;
|
||||
var dir = new List<Entry>();
|
||||
while (res_table_offset + 1 < file.MaxOffset)
|
||||
{
|
||||
int type_id = file.View.ReadUInt16 (res_table_offset);
|
||||
if (0 == type_id)
|
||||
break;
|
||||
string dir_name = null;
|
||||
if ((type_id & 0x8000) != 0)
|
||||
{
|
||||
type_id &= 0x7FFF;
|
||||
TypeMap.TryGetValue (type_id, out dir_name);
|
||||
}
|
||||
int count = file.View.ReadUInt16 (res_table_offset+2);
|
||||
res_table_offset += 8;
|
||||
if (null == dir_name)
|
||||
dir_name = string.Format ("#{0}", type_id);
|
||||
for (int i = 0; i < count; ++i)
|
||||
{
|
||||
int offset = file.View.ReadUInt16 (res_table_offset) << shift;
|
||||
uint size = (uint)file.View.ReadUInt16 (res_table_offset+2) << shift;
|
||||
int res_id = file.View.ReadUInt16 (res_table_offset+6);
|
||||
res_table_offset += 12;
|
||||
string name = res_id.ToString ("D5");
|
||||
name = string.Join ("/", dir_name, name);
|
||||
var entry = new Entry {
|
||||
Name = name,
|
||||
Offset = offset,
|
||||
Size = size,
|
||||
};
|
||||
dir.Add (entry);
|
||||
}
|
||||
}
|
||||
if (0 == dir.Count)
|
||||
return null;
|
||||
return new ArcFile (file, this, dir);
|
||||
}
|
||||
}
|
||||
}
|
@ -30,6 +30,7 @@ using System.ComponentModel.Composition;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
namespace GameRes.Formats.Sakana
|
||||
{
|
||||
@ -37,6 +38,7 @@ namespace GameRes.Formats.Sakana
|
||||
{
|
||||
public int Storage;
|
||||
public ushort Flags;
|
||||
public ushort ArcIndex;
|
||||
|
||||
public bool IsEncrypted { get { return 0 == (Flags & 0x10); } }
|
||||
}
|
||||
@ -57,11 +59,23 @@ namespace GameRes.Formats.Sakana
|
||||
public override bool CanWrite { get { return false; } }
|
||||
|
||||
const uint DefaultKey = 0x2E76034B;
|
||||
static readonly Regex ArchiveNameRe = new Regex (@"^(.*)-([^-]+)$");
|
||||
|
||||
public override ArcFile TryOpen (ArcView file)
|
||||
{
|
||||
var sx_name = FindSxName (file.Name);
|
||||
if (!VFS.FileExists (sx_name) || file.Name.Equals (sx_name, StringComparison.InvariantCultureIgnoreCase))
|
||||
var base_name = Path.GetFileNameWithoutExtension (file.Name);
|
||||
var sx_name = base_name.Substring (0, 4) + "(00).sx";
|
||||
sx_name = VFS.ChangeFileName (file.Name, sx_name);
|
||||
if (!VFS.FileExists (sx_name))
|
||||
{
|
||||
var match = ArchiveNameRe.Match (base_name);
|
||||
if (!match.Success)
|
||||
return null;
|
||||
sx_name = VFS.ChangeFileName (file.Name, match.Groups[1] + "(00).sx");
|
||||
if (!VFS.FileExists (sx_name))
|
||||
return null;
|
||||
}
|
||||
if (file.Name.Equals (sx_name, StringComparison.OrdinalIgnoreCase))
|
||||
return null;
|
||||
byte[] index_data;
|
||||
using (var sx = VFS.OpenView (sx_name))
|
||||
@ -86,6 +100,8 @@ namespace GameRes.Formats.Sakana
|
||||
{
|
||||
var reader = new SxIndexDeserializer (index, file.MaxOffset);
|
||||
var dir = reader.Deserialize();
|
||||
if (null == dir || dir.Count == 0)
|
||||
return null;
|
||||
return new ArcFile (file, this, dir);
|
||||
}
|
||||
}
|
||||
@ -103,7 +119,11 @@ namespace GameRes.Formats.Sakana
|
||||
DecryptData (input, key_lo, key_hi);
|
||||
}
|
||||
if (sx_entry.IsPacked)
|
||||
{
|
||||
input = UnpackZstd (input);
|
||||
if (sx_entry.UnpackedSize == 0)
|
||||
sx_entry.UnpackedSize = (uint)input.Length;
|
||||
}
|
||||
return new BinMemoryStream (input, entry.Name);
|
||||
}
|
||||
|
||||
@ -190,8 +210,7 @@ namespace GameRes.Formats.Sakana
|
||||
m_dir = new List<Entry> (count);
|
||||
for (int i = 0; i < count; ++i)
|
||||
{
|
||||
m_index.ReadByte();
|
||||
int storage = m_index.ReadByte();
|
||||
ushort arc = Binary.BigEndian (m_index.ReadUInt16());
|
||||
ushort flags = Binary.BigEndian (m_index.ReadUInt16());
|
||||
uint offset = Binary.BigEndian (m_index.ReadUInt32());
|
||||
uint size = Binary.BigEndian (m_index.ReadUInt32());
|
||||
@ -201,35 +220,32 @@ namespace GameRes.Formats.Sakana
|
||||
Offset = (long)offset << 4,
|
||||
Size = size,
|
||||
IsPacked = 0 != (flags & 0x03),
|
||||
ArcIndex = arc,
|
||||
};
|
||||
m_dir.Add (entry);
|
||||
}
|
||||
|
||||
count = Binary.BigEndian (m_index.ReadUInt16());
|
||||
var storages = new List<SxStorage>(count);
|
||||
for (int i = 0; i < count; ++i)
|
||||
int arc_count = Binary.BigEndian (m_index.ReadUInt16());
|
||||
int arc_index = -1;
|
||||
for (int i = 0; i < arc_count; ++i)
|
||||
{
|
||||
m_index.ReadUInt32();
|
||||
m_index.ReadUInt32();
|
||||
m_index.ReadUInt32();
|
||||
var storage = new SxStorage {
|
||||
Size = Binary.BigEndian (m_index.ReadUInt32()) << 4,
|
||||
Timestamp = Binary.BigEndian (m_index.ReadUInt64()),
|
||||
};
|
||||
storages.Add (storage);
|
||||
m_index.Seek (16, SeekOrigin.Current); // MD5
|
||||
long arc_size = (long)Binary.BigEndian (m_index.ReadUInt32()) << 4; // archive body length
|
||||
if (m_max_offset == arc_size)
|
||||
arc_index = i;
|
||||
m_index.ReadUInt64();
|
||||
m_index.Seek (16, SeekOrigin.Current); // MD5 sum
|
||||
}
|
||||
|
||||
count = Binary.BigEndian (m_index.ReadUInt16());
|
||||
if (count > 0)
|
||||
m_index.Seek (count * 24, SeekOrigin.Current);
|
||||
DeserializeTree();
|
||||
// Remove entries in other archives
|
||||
// Note using file size as archive identification can be problematic, but faster than MD5
|
||||
var current_storage = storages.FindIndex (s => m_max_offset == s.Size);
|
||||
if (-1 != current_storage)
|
||||
if (arc_count > 1 && arc_index != -1)
|
||||
{
|
||||
m_dir = m_dir.Where (e => e.CheckPlacement (m_max_offset) && (e as SxEntry).Storage == current_storage).ToList();
|
||||
return m_dir.Where (e => (e as SxEntry).ArcIndex == arc_index).ToList();
|
||||
}
|
||||
return m_dir;
|
||||
}
|
||||
|
@ -46,6 +46,11 @@ IN THE SOFTWARE.
|
||||
<scm:SortDescription PropertyName="Tag" Direction="Ascending"/>
|
||||
</CollectionViewSource.SortDescriptions>
|
||||
</CollectionViewSource>
|
||||
<CollectionViewSource x:Key="AudioFormatsSource" Source="{Binding Source={x:Static gr:FormatCatalog.Instance}, Path=AudioFormats, Mode=OneWay}">
|
||||
<CollectionViewSource.SortDescriptions>
|
||||
<scm:SortDescription PropertyName="Tag" Direction="Ascending"/>
|
||||
</CollectionViewSource.SortDescriptions>
|
||||
</CollectionViewSource>
|
||||
<Style x:Key="HiddenHeaderStyle" TargetType="{x:Type GridViewColumnHeader}">
|
||||
<Setter Property="Visibility" Value="Collapsed" />
|
||||
</Style>
|
||||
@ -126,7 +131,7 @@ IN THE SOFTWARE.
|
||||
<TabItem Header="{x:Static s:guiStrings.TextAboutAudio}">
|
||||
<Border BorderThickness="1" BorderBrush="Black" VerticalAlignment="Stretch" Margin="0" SnapsToDevicePixels="True">
|
||||
<ScrollViewer VerticalAlignment="Stretch" VerticalScrollBarVisibility="Auto" HorizontalScrollBarVisibility="Disabled" Margin="0" Background="{DynamicResource {x:Static SystemColors.ControlLightLightBrushKey}}">
|
||||
<ItemsControl Name="AudioFormats" ItemsSource="{Binding Source={x:Static gr:FormatCatalog.Instance}, Path=AudioFormats, Mode=OneWay}">
|
||||
<ItemsControl Name="AudioFormats" ItemsSource="{Binding Source={StaticResource AudioFormatsSource}}">
|
||||
<ItemsControl.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<StackPanel Orientation="Horizontal">
|
||||
|
@ -79,9 +79,9 @@ namespace GARbro.GUI
|
||||
public string Name { get; set; }
|
||||
public Entry Entry { get; set; }
|
||||
|
||||
public bool IsEqual (IEnumerable<string> path, string name)
|
||||
public bool IsEqual (IEnumerable<string> path, Entry entry)
|
||||
{
|
||||
return Path != null && path.SequenceEqual (Path) && name.Equals (Name);
|
||||
return Path != null && path.SequenceEqual (Path) && Entry == entry;
|
||||
}
|
||||
}
|
||||
|
||||
@ -139,7 +139,7 @@ namespace GARbro.GUI
|
||||
/// </summary>
|
||||
private void PreviewEntry (Entry entry)
|
||||
{
|
||||
if (m_current_preview.IsEqual (ViewModel.Path, entry.Name))
|
||||
if (m_current_preview.IsEqual (ViewModel.Path, entry))
|
||||
return;
|
||||
UpdatePreviewPane (entry);
|
||||
}
|
||||
|
@ -440,10 +440,23 @@ namespace GameRes
|
||||
{
|
||||
byte* s = m_mem + (offset - m_offset);
|
||||
uint string_length = 0;
|
||||
// enc.WindowsCodePage property might throw an exception for some encodings, while
|
||||
// CodePage is just a direct field access.
|
||||
if (enc.CodePage == 1200 || enc.CodePage == 1201) // for UTF-16 encodings stop marker is 2-bytes long
|
||||
{
|
||||
ushort* u = (ushort*)s;
|
||||
while (string_length + 1 < size && 0 != u[string_length >> 1])
|
||||
{
|
||||
string_length += 2;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
while (string_length < size && 0 != s[string_length])
|
||||
{
|
||||
++string_length;
|
||||
}
|
||||
}
|
||||
return new string ((sbyte*)s, 0, (int)string_length, enc);
|
||||
}
|
||||
}
|
||||
|
@ -263,7 +263,9 @@ namespace GameRes
|
||||
|
||||
public bool FileExists (string filename)
|
||||
{
|
||||
return m_dir.ContainsKey (CombinePath (CurrentDirectory, filename));
|
||||
return m_dir.ContainsKey (filename)
|
||||
|| !string.IsNullOrEmpty (CurrentDirectory)
|
||||
&& m_dir.ContainsKey (CombinePath (CurrentDirectory, filename));
|
||||
}
|
||||
|
||||
public Stream OpenStream (Entry entry)
|
||||
@ -408,6 +410,8 @@ namespace GameRes
|
||||
Entry entry = null;
|
||||
if (m_dir.TryGetValue (filename, out entry))
|
||||
return entry;
|
||||
if (m_dir.TryGetValue (CombinePath (CurrentDirectory, filename), out entry))
|
||||
return entry;
|
||||
var dir_name = filename + PathDelimiter;
|
||||
if (m_dir.Keys.Any (n => n.StartsWith (dir_name)))
|
||||
return new SubDirEntry (filename);
|
||||
|
@ -220,6 +220,13 @@ namespace GameRes
|
||||
file.Read (pixels, dst, stride);
|
||||
for (int x = 3; !has_alpha && x < stride; x += 4)
|
||||
has_alpha = pixels[dst+x] != 0;
|
||||
/* // sometimes alpha channel is inverted
|
||||
for (int x = 3; x < stride; x += 4)
|
||||
{
|
||||
pixels[dst+x] ^= 0xFF;
|
||||
has_alpha = has_alpha || pixels[dst+x] != 0;
|
||||
}
|
||||
*/
|
||||
}
|
||||
PixelFormat format = has_alpha ? PixelFormats.Bgra32 : PixelFormats.Bgr32;
|
||||
return ImageData.Create (info, format, null, pixels, stride);
|
||||
|
@ -122,7 +122,7 @@ namespace GameRes
|
||||
protected ImageData m_image;
|
||||
|
||||
public Stream Source { get { m_input.Position = 0; return m_input.AsStream; } }
|
||||
public ImageFormat SourceFormat { get { return null; } }
|
||||
public ImageFormat SourceFormat { get; protected set; }
|
||||
public ImageMetaData Info { get; protected set; }
|
||||
public ImageData Image { get { return m_image ?? (m_image = GetImageData()); } }
|
||||
|
||||
|
@ -26,7 +26,7 @@
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.Composition;
|
||||
|
||||
// [000225][Light Plan] My Fairink Yousei Byakuya Monogatari
|
||||
// [000225][Light Plan] My Fair Link Yousei Byakuya Monogatari
|
||||
|
||||
namespace GameRes.Formats.Gsx
|
||||
{
|
||||
|
70
Legacy/Gsx/ArcK5.cs
Normal file
70
Legacy/Gsx/ArcK5.cs
Normal file
@ -0,0 +1,70 @@
|
||||
//! \file ArcK5.cs
|
||||
//! \date 2023 Aug 27
|
||||
//! \brief GSX engine resource archive.
|
||||
//
|
||||
// Copyright (C) 2023 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.Collections.Generic;
|
||||
using System.ComponentModel.Composition;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
|
||||
namespace GameRes.Formats.Gsx
|
||||
{
|
||||
[Export(typeof(ArchiveFormat))]
|
||||
public class K5Opener : ArchiveFormat
|
||||
{
|
||||
public override string Tag => "K5";
|
||||
public override string Description => "GSX engine resource archive";
|
||||
public override uint Signature => 0x01354B; // 'K5'
|
||||
public override bool IsHierarchic => true;
|
||||
public override bool CanWrite => false;
|
||||
|
||||
public K5Opener ()
|
||||
{
|
||||
ContainedFormats = new[] { "K4", "OGG" };
|
||||
}
|
||||
|
||||
public override ArcFile TryOpen (ArcView file)
|
||||
{
|
||||
int count = file.View.ReadInt32 (4);
|
||||
if (!IsSaneCount (count))
|
||||
return null;
|
||||
uint index_offset = file.View.ReadUInt32 (8);
|
||||
var dir = new List<Entry> (count);
|
||||
for (int i = 0; i < count; ++i)
|
||||
{
|
||||
var dir_name = file.View.ReadString (index_offset, 0x80, Encoding.Unicode);
|
||||
var name = file.View.ReadString (index_offset+0x80, 0x40, Encoding.Unicode);
|
||||
name = Path.Combine (dir_name, name);
|
||||
var entry = Create<Entry> (name);
|
||||
entry.Offset = file.View.ReadUInt32 (index_offset+0xC8);
|
||||
entry.Size = file.View.ReadUInt32 (index_offset+0xCC);
|
||||
if (!entry.CheckPlacement (file.MaxOffset))
|
||||
return null;
|
||||
dir.Add (entry);
|
||||
index_offset += 0x100;
|
||||
}
|
||||
return new ArcFile (file, this, dir);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,8 +1,8 @@
|
||||
//! \file ImageK4.cs
|
||||
//! \date 2019 Feb 07
|
||||
//! \brief Toyo GSX image format.
|
||||
//! \date 2023 Aug 27
|
||||
//! \brief GSX engine image format.
|
||||
//
|
||||
// Copyright (C) 2019 by morkt
|
||||
// Copyright (C) 2023 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
|
||||
@ -23,6 +23,8 @@
|
||||
// IN THE SOFTWARE.
|
||||
//
|
||||
|
||||
using GameRes.Utility;
|
||||
using System;
|
||||
using System.ComponentModel.Composition;
|
||||
using System.IO;
|
||||
using System.Windows.Media;
|
||||
@ -33,16 +35,16 @@ namespace GameRes.Formats.Gsx
|
||||
{
|
||||
internal class K4MetaData : ImageMetaData
|
||||
{
|
||||
public bool HasAlpha;
|
||||
public byte AlphaMode;
|
||||
public int FrameCount;
|
||||
}
|
||||
|
||||
[Export(typeof(ImageFormat))]
|
||||
public class K4Format : ImageFormat
|
||||
{
|
||||
public override string Tag { get { return "K4"; } }
|
||||
public override string Description { get { return "Toyo GSX image format"; } }
|
||||
public override uint Signature { get { return 0x0201344B; } } // 'K4'
|
||||
public override string Tag => "K4";
|
||||
public override string Description => "GSX engine image format";
|
||||
public override uint Signature => 0x0201344B; // 'K4'
|
||||
|
||||
public override ImageMetaData ReadMetaData (IBinaryStream file)
|
||||
{
|
||||
@ -51,20 +53,22 @@ namespace GameRes.Formats.Gsx
|
||||
return null;
|
||||
if (header[2] != 1 || header[3] != 2)
|
||||
return null;
|
||||
int frame_count = header.ToInt16 (0xC);
|
||||
if (frame_count <= 0)
|
||||
return null;
|
||||
return new K4MetaData {
|
||||
Width = header.ToUInt16 (4),
|
||||
Height = header.ToUInt16 (6),
|
||||
BPP = header[0xF],
|
||||
HasAlpha = header[0xB] != 0,
|
||||
FrameCount = header.ToUInt16 (0xC),
|
||||
AlphaMode = header[0xB],
|
||||
FrameCount = frame_count,
|
||||
};
|
||||
}
|
||||
|
||||
public override ImageData Read (IBinaryStream file, ImageMetaData info)
|
||||
{
|
||||
var meta = (K4MetaData)info;
|
||||
|
||||
return ImageData.Create (info, format, palette, pixels);
|
||||
var reader = new K4Reader (file, (K4MetaData)info);
|
||||
return reader.Unpack();
|
||||
}
|
||||
|
||||
public override void Write (Stream file, ImageData image)
|
||||
@ -72,4 +76,190 @@ namespace GameRes.Formats.Gsx
|
||||
throw new System.NotImplementedException ("K4Format.Write not implemented");
|
||||
}
|
||||
}
|
||||
|
||||
internal sealed class K4Reader
|
||||
{
|
||||
IBinaryStream m_input;
|
||||
K4MetaData m_info;
|
||||
|
||||
public K4Reader (IBinaryStream input, K4MetaData info)
|
||||
{
|
||||
m_input = input;
|
||||
m_info = info;
|
||||
}
|
||||
|
||||
int m_stride;
|
||||
int m_pixel_size;
|
||||
|
||||
public ImageData Unpack ()
|
||||
{
|
||||
uint base_offset = 0x30;
|
||||
m_input.Position = base_offset;
|
||||
m_info.Width = m_input.ReadUInt16();
|
||||
m_info.Height = m_input.ReadUInt16();
|
||||
m_input.Seek (8, SeekOrigin.Current);
|
||||
int bpp = m_input.ReadUInt16();
|
||||
int flags = m_input.ReadUInt16();
|
||||
m_input.Seek (4, SeekOrigin.Current);
|
||||
uint alpha_pos = m_input.ReadUInt32();
|
||||
m_input.Seek (12, SeekOrigin.Current);
|
||||
int ctl_length = m_input.ReadInt32();
|
||||
|
||||
m_pixel_size = bpp / 8;
|
||||
m_stride = (m_info.iWidth * m_pixel_size + 3) & ~3;
|
||||
var pixels = new byte[m_info.iHeight * m_stride];
|
||||
int dst = 0;
|
||||
bool do_delta = (flags & 1) != 0;
|
||||
var control_bytes = m_input.ReadBytes (ctl_length - 0x10);
|
||||
using (var mem = new MemoryStream (control_bytes))
|
||||
using (var ctl = new MsbBitStream (mem))
|
||||
using (var data = new MsbBitStream (m_input.AsStream, true))
|
||||
{
|
||||
while (dst < pixels.Length)
|
||||
{
|
||||
int b = ctl.GetNextBit();
|
||||
if (-1 == b)
|
||||
break;
|
||||
if (b != 0)
|
||||
{
|
||||
if (!do_delta)
|
||||
{
|
||||
pixels[dst++] = (byte)data.GetBits (8);
|
||||
}
|
||||
else if (dst >= m_pixel_size)
|
||||
{
|
||||
pixels[dst] = (byte)(pixels[dst - m_pixel_size] + data.GetBits (9) + 1);
|
||||
++dst;
|
||||
}
|
||||
else
|
||||
{
|
||||
pixels[dst++] = (byte)data.GetBits (9);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
int pos, count;
|
||||
if (ctl.GetNextBit() != 0)
|
||||
{
|
||||
pos = data.GetBits (14);
|
||||
count = data.GetBits (4) + 3;
|
||||
}
|
||||
else
|
||||
{
|
||||
pos = data.GetBits (9);
|
||||
count = data.GetBits (3) + 2;
|
||||
}
|
||||
int src = dst - pos - 1;
|
||||
count = Math.Min (count, pixels.Length - dst);
|
||||
if (!do_delta || dst < m_pixel_size)
|
||||
{
|
||||
Binary.CopyOverlapped (pixels, src, dst, count);
|
||||
dst += count;
|
||||
}
|
||||
else
|
||||
{
|
||||
while (count --> 0)
|
||||
{
|
||||
pixels[dst++] = (byte)(pixels[src] + pixels[src + pos - m_pixel_size + 1] - pixels[src - m_pixel_size]);
|
||||
++src;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (0 == alpha_pos)
|
||||
{
|
||||
if (24 == bpp)
|
||||
return ImageData.CreateFlipped (m_info, PixelFormats.Bgr24, null, pixels, m_stride);
|
||||
else
|
||||
return ImageData.CreateFlipped (m_info, PixelFormats.Bgr32, null, pixels, m_stride);
|
||||
}
|
||||
if (0xFF == m_info.AlphaMode)
|
||||
pixels = UnpackAlphaFF (alpha_pos + base_offset, pixels);
|
||||
else if (0xFE == m_info.AlphaMode)
|
||||
pixels = UnpackAlphaFE (alpha_pos + base_offset, pixels);
|
||||
else
|
||||
throw new NotSupportedException (string.Format ("Not supported alpha channel mode 0x{0:X2}", m_info.AlphaMode));
|
||||
m_stride = m_info.iWidth * 4;
|
||||
return ImageData.Create (m_info, PixelFormats.Bgra32, null, pixels, m_stride);
|
||||
}
|
||||
|
||||
byte[] UnpackAlphaFF (uint alpha_pos, byte[] pixels)
|
||||
{
|
||||
m_input.Position = alpha_pos;
|
||||
var offsets = new int[m_info.iHeight];
|
||||
for (int i = 0; i < offsets.Length; ++i)
|
||||
offsets[i] = m_input.ReadInt32();
|
||||
|
||||
var output = new byte[m_info.iWidth * m_info.iHeight * 4];
|
||||
int dst = 0;
|
||||
for (int y = 0; y < m_info.iHeight; y++)
|
||||
{
|
||||
m_input.Position = alpha_pos + offsets[y];
|
||||
int src = (m_info.iHeight - y - 1) * m_stride;
|
||||
int dst_a = dst + 3;
|
||||
for (int x = 0; x < m_info.iWidth; ++x)
|
||||
{
|
||||
output[dst ] = pixels[src ];
|
||||
output[dst+1] = pixels[src+1];
|
||||
output[dst+2] = pixels[src+2];
|
||||
dst += 4;
|
||||
src += m_pixel_size;
|
||||
}
|
||||
for (int x = 0; x < m_info.iWidth; )
|
||||
{
|
||||
byte alpha = m_input.ReadUInt8();
|
||||
int count = m_input.ReadUInt8();
|
||||
count = Math.Min (count, m_info.iWidth - x);
|
||||
x += count;
|
||||
if (alpha > 0)
|
||||
{
|
||||
alpha = (byte)((alpha * 0xFF) >> 7);
|
||||
while (count --> 0)
|
||||
{
|
||||
output[dst_a] = alpha;
|
||||
dst_a += 4;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
dst_a += 4 * count;
|
||||
}
|
||||
}
|
||||
}
|
||||
return output;
|
||||
}
|
||||
|
||||
byte[] UnpackAlphaFE (uint alpha_pos, byte[] pixels)
|
||||
{
|
||||
m_input.Position = alpha_pos;
|
||||
var output = new byte[m_info.iWidth * m_info.iHeight * 4];
|
||||
int dst = 0;
|
||||
for (int y = 0; y < m_info.iHeight; y++)
|
||||
{
|
||||
int src = (m_info.iHeight - y - 1) * m_stride;
|
||||
int dst_a = dst + 3;
|
||||
for (int x = 0; x < m_info.iWidth; ++x)
|
||||
{
|
||||
output[dst ] = pixels[src ];
|
||||
output[dst+1] = pixels[src+1];
|
||||
output[dst+2] = pixels[src+2];
|
||||
dst += 4;
|
||||
src += m_pixel_size;
|
||||
}
|
||||
for (int x = 0; x < m_info.iWidth; x += 8)
|
||||
{
|
||||
byte alpha = m_input.ReadUInt8();
|
||||
int count = Math.Min (8, m_info.iWidth - x);
|
||||
for (int i = 0; i < count; ++i)
|
||||
{
|
||||
output[dst_a] = (byte)-(alpha & 1);
|
||||
dst_a += 4;
|
||||
alpha >>= 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
return output;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
421
Legacy/HyperWorks/ImageG.cs
Normal file
421
Legacy/HyperWorks/ImageG.cs
Normal file
@ -0,0 +1,421 @@
|
||||
//! \file ImageG.cs
|
||||
//! \date 2023 Aug 28
|
||||
//! \brief HyperWorks image format.
|
||||
//
|
||||
// Copyright (C) 2023 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;
|
||||
|
||||
// [951207][Love Gun] ACE OF SPADES
|
||||
// I24 predecessor
|
||||
|
||||
namespace GameRes.Formats.HyperWorks
|
||||
{
|
||||
[Export(typeof(ImageFormat))]
|
||||
public class GFormat : ImageFormat
|
||||
{
|
||||
public override string Tag => "G";
|
||||
public override string Description => "HyperWorks indexed image format";
|
||||
public override uint Signature => 0x1A477D00;
|
||||
|
||||
public GFormat ()
|
||||
{
|
||||
Signatures = new[] { 0x1A477D00u, 0u };
|
||||
}
|
||||
|
||||
public override ImageMetaData ReadMetaData (IBinaryStream file)
|
||||
{
|
||||
var header = file.ReadHeader (12);
|
||||
if (header.ToUInt16 (2) != 0x1A47)
|
||||
return null;
|
||||
// not sure if 0x7D00 is a required signature, so rely on filename
|
||||
if (header.ToUInt16 (0) != 0x7D00 && !file.Name.HasExtension (".G"))
|
||||
return null;
|
||||
return new ImageMetaData {
|
||||
Width = header.ToUInt16 (8),
|
||||
Height = header.ToUInt16 (10),
|
||||
OffsetX = header.ToInt16 (4),
|
||||
OffsetY = header.ToInt16 (6),
|
||||
BPP = 8,
|
||||
};
|
||||
}
|
||||
|
||||
public override ImageData Read (IBinaryStream file, ImageMetaData info)
|
||||
{
|
||||
var reader = new GReader (file, info);
|
||||
return reader.Unpack();
|
||||
}
|
||||
|
||||
public override void Write (Stream file, ImageData image)
|
||||
{
|
||||
throw new System.NotImplementedException ("GFormat.Write not implemented");
|
||||
}
|
||||
}
|
||||
|
||||
internal sealed class GReader
|
||||
{
|
||||
IBinaryStream m_input;
|
||||
ImageMetaData m_info;
|
||||
int m_stride;
|
||||
byte[] m_palette_data;
|
||||
byte[] m_output;
|
||||
ushort[] m_buffer;
|
||||
int[] m_line_ptr;
|
||||
|
||||
public GReader (IBinaryStream input, ImageMetaData info)
|
||||
{
|
||||
m_input = input;
|
||||
m_info = info;
|
||||
m_stride = (m_info.iWidth * m_info.BPP / 8 + 1) & -2;
|
||||
int s = Math.Max (0x142, m_stride / 2 + 2); // line buffer size
|
||||
m_buffer = new ushort[s * 3 + 1];
|
||||
m_line_ptr = new int[3] { 1, 1 + s, 1 + s*2 };
|
||||
}
|
||||
|
||||
public ImageData Unpack ()
|
||||
{
|
||||
m_input.Position = 0x0C;
|
||||
m_palette_data = m_input.ReadBytes (0x30);
|
||||
m_input.Position = 0x40;
|
||||
int width = ((m_info.iWidth + 7) & -8);
|
||||
int rows = ((m_info.iHeight + 1) & -2);
|
||||
m_output = new byte[m_stride * rows];
|
||||
InitColorTable();
|
||||
int blockW = width >> 1;
|
||||
int blockH = rows >> 1;
|
||||
SetupBitReader();
|
||||
for (int y = 0; y < blockH; ++y)
|
||||
{
|
||||
var dst = m_line_ptr[2];
|
||||
m_line_ptr[2] = m_line_ptr[1];
|
||||
m_line_ptr[1] = m_line_ptr[0];
|
||||
m_line_ptr[0] = dst;
|
||||
int x = 0;
|
||||
while (x < blockW)
|
||||
{
|
||||
if (GetNextBit() != 0)
|
||||
{
|
||||
m_buffer[dst++] = GetColorFromTable (x);
|
||||
++x;
|
||||
}
|
||||
else
|
||||
{
|
||||
int count = ExtractBits (BitTable1);
|
||||
if (count >= 0x40)
|
||||
count += ExtractBits (BitTable1);
|
||||
int idx = ExtractBits (BitTable2) * 2;
|
||||
int src = m_line_ptr[OffTable[idx + 1]];
|
||||
src += OffTable[idx] + x;
|
||||
x += count;
|
||||
while (count --> 0)
|
||||
{
|
||||
m_buffer[dst++] = m_buffer[src++];
|
||||
}
|
||||
}
|
||||
}
|
||||
UnpackRow (y, blockW, m_line_ptr[0]);
|
||||
}
|
||||
var palette = UnpackPalette();
|
||||
return ImageData.Create (m_info, PixelFormats.Indexed8, palette, m_output, m_stride);
|
||||
}
|
||||
|
||||
void UnpackRow (int y, int width, int buf_pos)
|
||||
{
|
||||
int row1 = m_stride * y * 2;
|
||||
int row2 = row1 + m_stride;
|
||||
for (int i = 0; i < width; ++i)
|
||||
{
|
||||
ushort v = m_buffer[buf_pos++];
|
||||
ushort v0 = (ushort)((v & 0xF00 | ((v & 0xF000) >> 12)) + 0xA0A);
|
||||
LittleEndian.Pack (v0, m_output, row2);
|
||||
row2 += 2;
|
||||
ushort v1 = (ushort)((((v & 0xF) << 8) | ((v & 0xF0) >> 4)) + 0xA0A);
|
||||
LittleEndian.Pack (v1, m_output, row1);
|
||||
row1 += 2;
|
||||
}
|
||||
}
|
||||
|
||||
byte[] g_palIndexes = { 0, 1, 4, 5, 2, 3, 6, 7, 8, 9, 0xC, 0xD, 0xA, 0xB, 0xE, 0xF };
|
||||
|
||||
BitmapPalette UnpackPalette ()
|
||||
{
|
||||
var colors = new Color[42];
|
||||
for (int i = 0; i < 16; ++i)
|
||||
{
|
||||
int R = m_palette_data[3 * g_palIndexes[i] ]; R |= R << 4;
|
||||
int G = m_palette_data[3 * g_palIndexes[i] + 1]; G |= G << 4;
|
||||
int B = m_palette_data[3 * g_palIndexes[i] + 2]; B |= B << 4;
|
||||
colors[i+10] = Color.FromRgb ((byte)R, (byte)G, (byte)B);
|
||||
|
||||
int b = R & 1;
|
||||
int c = (sbyte)R >> 1;
|
||||
if (c < 0)
|
||||
c += b;
|
||||
colors[i+26].R = (byte)c;
|
||||
|
||||
b = G & 1;
|
||||
c = (sbyte)G >> 1;
|
||||
if (c < 0)
|
||||
c += b;
|
||||
colors[i+26].G = (byte)c;
|
||||
|
||||
b = B & 1;
|
||||
c = (sbyte)B >> 1;
|
||||
if (c < 0)
|
||||
c += b;
|
||||
colors[i+26].B = (byte)c;
|
||||
}
|
||||
return new BitmapPalette (colors);
|
||||
}
|
||||
|
||||
byte[] g_colorTable = new byte[256];
|
||||
|
||||
void InitColorTable ()
|
||||
{
|
||||
int dst = 0;
|
||||
for (int i = 0; i < 16; ++i)
|
||||
for (int j = 0; j < 16; ++j)
|
||||
g_colorTable[dst++] = (byte)((j + i + 1) & 0xF);
|
||||
}
|
||||
|
||||
ushort GetColorFromTable (int x)
|
||||
{
|
||||
ushort b0 = m_buffer[m_line_ptr[1] + x];
|
||||
int n0 = b0 & 0xF;
|
||||
int n1 = (b0 >> 4) & 0xF;
|
||||
int n2 = (b0 >> 8) & 0xF;
|
||||
int n3 = (b0 >> 12) & 0xF;
|
||||
|
||||
ushort b1 = m_buffer[m_line_ptr[1] + x - 1];
|
||||
int m0 = b1 & 0xF;
|
||||
int m1 = (b1 >> 4) & 0xF;
|
||||
int m2 = (b1 >> 8) & 0xF;
|
||||
int m3 = (b1 >> 12) & 0xF;
|
||||
|
||||
ushort b2 = m_buffer[m_line_ptr[0] + x - 1];
|
||||
int p0 = b2 & 0xF;
|
||||
int p1 = (b2 >> 4) & 0xF;
|
||||
int p2 = (b2 >> 8) & 0xF;
|
||||
int p3 = (b2 >> 12) & 0xF;
|
||||
|
||||
int r1 = n1;
|
||||
if (n1 != n3 && (n1 != m1 || n1 != p1))
|
||||
{
|
||||
if (p0 == p1)
|
||||
r1 = p0;
|
||||
else
|
||||
r1 = m2;
|
||||
}
|
||||
if (GetNextBit() != 0)
|
||||
r1 = AdjustColorTable (r1);
|
||||
|
||||
int r0 = n0;
|
||||
if (n0 != n2 && (n0 != m0 || n0 != p0))
|
||||
{
|
||||
if (r1 == p1)
|
||||
r0 = p1;
|
||||
else
|
||||
r0 = n3;
|
||||
}
|
||||
if (GetNextBit() != 0)
|
||||
r0 = AdjustColorTable (r0);
|
||||
|
||||
int r3 = n3;
|
||||
if (r1 != n3 && (n3 != m3 || n3 != p3))
|
||||
{
|
||||
if (p2 == p3)
|
||||
r3 = p2;
|
||||
else
|
||||
r3 = p0;
|
||||
}
|
||||
if (GetNextBit() != 0)
|
||||
r3 = AdjustColorTable (r3);
|
||||
|
||||
int r2 = n2;
|
||||
if (n2 != r0 && (n2 != m2 || n2 != p2))
|
||||
{
|
||||
if (p3 == r3)
|
||||
r2 = p3;
|
||||
else
|
||||
r2 = r1;
|
||||
}
|
||||
if (GetNextBit() != 0)
|
||||
r2 = AdjustColorTable (r2);
|
||||
|
||||
return (ushort)((r3 << 12) | (r2 << 8) | (r1 << 4) | r0);
|
||||
}
|
||||
|
||||
byte AdjustColorTable (int idx)
|
||||
{
|
||||
int shift_count = ExtractBits (BitTable3);
|
||||
int i = 16 * idx + shift_count;
|
||||
byte c = g_colorTable[i];
|
||||
if (shift_count != 0)
|
||||
{
|
||||
while (shift_count --> 0)
|
||||
{
|
||||
g_colorTable[i] = g_colorTable[i-1];
|
||||
--i;
|
||||
}
|
||||
g_colorTable[i] = c;
|
||||
}
|
||||
return c;
|
||||
}
|
||||
|
||||
int ExtractBits (byte[] table)
|
||||
{
|
||||
int idx = ((bits >> 8) & 0xFF) << 1;
|
||||
int n = table[idx];
|
||||
if (n != 0)
|
||||
{
|
||||
if (n >= bitCount)
|
||||
{
|
||||
n -= bitCount;
|
||||
bits <<= bitCount;
|
||||
int b = m_input.ReadByte();
|
||||
if (b != -1) // XXX ignore EOF
|
||||
bits |= b;
|
||||
bitCount = 8;
|
||||
}
|
||||
bits <<= n;
|
||||
bitCount -= n;
|
||||
return table[idx+1];
|
||||
}
|
||||
else
|
||||
{
|
||||
bits <<= bitCount;
|
||||
int b = m_input.ReadByte();
|
||||
if (b != -1) // XXX ignore EOF
|
||||
bits |= b;
|
||||
bits <<= 8 - bitCount;
|
||||
int t = table[idx+1];
|
||||
do
|
||||
{
|
||||
int i = GetNextBit();
|
||||
t = OffTable[2 * t + 54 + i];
|
||||
}
|
||||
while (OffTable[2 * t + 54] != 0);
|
||||
return OffTable[2 * t + 55];
|
||||
}
|
||||
}
|
||||
|
||||
int bits;
|
||||
int bitCount;
|
||||
|
||||
private void SetupBitReader ()
|
||||
{
|
||||
bits = m_input.ReadUInt8() << 8;
|
||||
bits |= m_input.ReadUInt8();
|
||||
bitCount = 8;
|
||||
}
|
||||
|
||||
private int GetNextBit ()
|
||||
{
|
||||
bits <<= 1;
|
||||
if (0 == --bitCount)
|
||||
{
|
||||
int b = m_input.ReadByte();
|
||||
if (b != -1) // XXX ignore EOF
|
||||
bits |= b;
|
||||
bitCount = 8;
|
||||
}
|
||||
return (bits >> 16) & 1;
|
||||
}
|
||||
|
||||
static readonly byte[] BitTable1 = new byte[] {
|
||||
3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3,
|
||||
2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 8,
|
||||
0xD, 0, 0, 0, 1, 8, 0xE, 6, 7, 6, 7, 6, 7, 6, 7, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
|
||||
7, 0xA, 7, 0xA, 0, 2, 0, 7, 7, 0xB, 7, 0xB, 8, 0xF, 0, 3, 6, 8, 6, 8, 6, 8, 6, 8, 8, 0x10, 0, 4,
|
||||
0, 5, 8, 0x11, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
|
||||
4, 4, 4, 4, 5, 6, 5, 6, 5, 6, 5, 6, 5, 6, 5, 6, 5, 6, 5, 6, 7, 0xC, 7, 0xC, 0, 8, 0, 6, 6, 9, 6,
|
||||
9, 6, 9, 6, 9, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
|
||||
3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
|
||||
3, 3, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
};
|
||||
static readonly byte[] BitTable2 = new byte[] {
|
||||
5, 4, 5, 4, 5, 4, 5, 4, 5, 4, 5, 4, 5, 4, 5, 4, 7, 0x17, 7, 0x17, 7, 0x16, 7, 0x16, 6, 0x0E, 6,
|
||||
0x0E, 6, 0x0E, 6, 0x0E, 4, 3, 4, 3, 4, 3, 4, 3, 4, 3, 4, 3, 4, 3, 4, 3, 4, 3, 4, 3, 4, 3, 4, 3, 4,
|
||||
3, 4, 3, 4, 3, 4, 3, 6, 0x0D, 6, 0x0D, 6, 0x0D, 6, 0x0D, 6, 0x0C, 6, 0x0C, 6, 0x0C, 6, 0x0C, 5, 6,
|
||||
5, 6, 5, 6, 5, 6, 5, 6, 5, 6, 5, 6, 5, 6, 5, 7, 5, 7, 5, 7, 5, 7, 5, 7, 5, 7, 5, 7, 5, 7, 5, 5, 5,
|
||||
5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 8, 5, 8, 5, 8, 5, 8, 5, 8, 5, 8, 5, 8, 5, 8, 7, 0x14, 7,
|
||||
0x14, 7, 0x18, 7, 0x18, 6, 0x10, 6, 0x10, 6, 0x10, 6, 0x10, 6, 0x11, 6, 0x11, 6, 0x11, 6, 0x11, 6,
|
||||
0x0F, 6, 0x0F, 6, 0x0F, 6, 0x0F, 5, 0x0A, 5, 0x0A, 5, 0x0A, 5, 0x0A, 5, 0x0A, 5, 0x0A, 5, 0x0A, 5,
|
||||
0x0A, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3,
|
||||
1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, 1,
|
||||
6, 0x13, 6, 0x13, 6, 0x13, 6, 0x13, 6, 0x12, 6, 0x12, 6, 0x12, 6, 0x12, 5, 0x0B, 5, 0x0B, 5, 0x0B,
|
||||
5, 0x0B, 5, 0x0B, 5, 0x0B, 5, 0x0B, 5, 0x0B, 7, 0x19, 7, 0x19, 7, 0x1A, 7, 0x1A, 6, 0x15, 6, 0x15,
|
||||
6, 0x15, 6, 0x15, 5, 9, 5, 9, 5, 9, 5, 9, 5, 9, 5, 9, 5, 9, 5, 9, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3,
|
||||
2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2,
|
||||
3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 2, 0, 2, 0, 2, 0, 2, 0, 2, 0, 2, 0, 2,
|
||||
0, 2, 0, 2, 0, 2, 0, 2, 0, 2, 0, 2, 0, 2, 0, 2, 0, 2, 0, 2, 0, 2, 0, 2, 0, 2, 0, 2, 0, 2, 0, 2, 0,
|
||||
2, 0, 2, 0, 2, 0, 2, 0, 2, 0, 2, 0, 2, 0, 2, 0, 2, 0, 2, 0, 2, 0, 2, 0, 2, 0, 2, 0, 2, 0, 2, 0, 2,
|
||||
0, 2, 0, 2, 0, 2, 0, 2, 0, 2, 0, 2, 0, 2, 0, 2, 0, 2, 0, 2, 0, 2, 0, 2, 0, 2, 0, 2, 0, 2, 0, 2, 0,
|
||||
2, 0, 2, 0, 2, 0, 2, 0, 2, 0, 2, 0, 2, 0, 2, 0,
|
||||
};
|
||||
static readonly byte[] BitTable3 = new byte[] {
|
||||
4, 2, 4, 2, 4, 2, 4, 2, 4, 2, 4, 2, 4, 2, 4, 2, 4, 2, 4, 2, 4, 2, 4, 2, 4, 2, 4, 2, 4, 2, 4, 2, 5,
|
||||
4, 5, 4, 5, 4, 5, 4, 5, 4, 5, 4, 5, 4, 5, 4, 6, 6, 6, 6, 6, 6, 6, 6, 7, 8, 7, 8, 8, 0x0A, 8, 0x0B,
|
||||
4, 3, 4, 3, 4, 3, 4, 3, 4, 3, 4, 3, 4, 3, 4, 3, 4, 3, 4, 3, 4, 3, 4, 3, 4, 3, 4, 3, 4, 3, 4, 3, 5,
|
||||
5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 6, 7, 6, 7, 6, 7, 6, 7, 7, 9, 7, 9, 0, 0x5F, 8, 0x0C,
|
||||
2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2,
|
||||
1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1,
|
||||
2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2,
|
||||
1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 1, 0, 1, 0,
|
||||
1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1,
|
||||
0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0,
|
||||
1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1,
|
||||
0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0,
|
||||
1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1,
|
||||
0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0,
|
||||
1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1,
|
||||
0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0,
|
||||
};
|
||||
static readonly short[] OffTable = new short[] {
|
||||
0, 1, 1, 1, -1, 0, -1, 1, -4, 0, -2, 0, 2, 1, -2, 1, 4, 1, 0, 2, 1, 2, -1, 2, -8, 0, -4, 1, 8, 1,
|
||||
-2, 2, 2, 2, -4, 2, 4, 2, 8, 2, -8, 1, -0x10, 0, -8, 2, 0x10, 1, 0x10, 2, -0x10, 1, -0x10, 2,
|
||||
0x2C, 9, 0x2D, 0x0A, 0x2E, 0x0B, 0x2F, 0x0C, 0x30, 0x0D, 0x31, 0x32, 0x0E, 0x33, 0x0F, 0x10, 0x11,
|
||||
0x12, 0x13, 0x14, 0x34, 0x15, 0x37, 0x35, 0x16, 0x38, 0x17, 0x39, 0x3D, 0x18, 0x19, 0x36, 0x1A,
|
||||
0x1B, 0x3A, 0x3C, 0x1C, 0x3B, 0x1D, 0x3E, 0x3F, 0x1E, 0x40, 0x1F, 0x45, 0x46, 0x43, 0x20, 0x21,
|
||||
0x48, 0x22, 0x41, 0x23, 0x42, 0x24, 0x25, 0x44, 0x47, 0x4F, 0x4A, 0x49, 0x53, 0x4B, 0x4D, 0x57,
|
||||
0x52, 0x59, 0x58, 0x4E, 0x50, 0x56, 0x26, 0x54, 0x4C, 0x51, 0x55, 0x27, 0x28, 0x5A, 0x2B, 0x29,
|
||||
0x5B, 0x2A, 0x5C, 0x5D, 0x5E, 0, 0, 0, 0x12, 0, 0x13, 0, 0x14, 0, 0x15, 0, 0x16, 0, 0x17, 0, 0x18,
|
||||
0, 0x19, 0, 0x1A, 0, 0x1B, 0, 0x1C, 0, 0x1D, 0, 0x1E, 0, 0x1F, 0, 0x20, 0, 0x21, 0, 0x22, 0, 0x23,
|
||||
0, 0x24, 0, 0x25, 0, 0x26, 0, 0x27, 0, 0x28, 0, 0x29, 0, 0x2A, 0, 0x2B, 0, 0x2C, 0, 0x2D, 0, 0x2E,
|
||||
0, 0x2F, 0, 0x30, 0, 0x31, 0, 0x32, 0, 0x33, 0, 0x34, 0, 0x35, 0, 0x36, 0, 0x37, 0, 0x38, 0, 0x39,
|
||||
0, 0x3A, 0, 0x3B, 0, 0x3C, 0, 0x3D, 0, 0x3E, 0, 0x3F, 0, 0x40, 0, 0x80, 0, 0x0C0, 0, 0x100, 0,
|
||||
0x140, 0x60, 0x61, 0, 0x0D, 0, 0x0E,
|
||||
};
|
||||
}
|
||||
}
|
@ -23,22 +23,32 @@
|
||||
// IN THE SOFTWARE.
|
||||
//
|
||||
|
||||
using GameRes.Utility;
|
||||
using System;
|
||||
using System.ComponentModel.Composition;
|
||||
using System.IO;
|
||||
using System.Windows.Media;
|
||||
|
||||
// [970905][Ucom] Winter Kiss
|
||||
// [980626][Love Gun] ACE OF SPADES 2
|
||||
|
||||
namespace GameRes.Formats.HyperWorks
|
||||
{
|
||||
internal class I24MetaData : ImageMetaData
|
||||
{
|
||||
public byte Version;
|
||||
}
|
||||
|
||||
[Export(typeof(ImageFormat))]
|
||||
public class I24Format : ImageFormat
|
||||
{
|
||||
public override string Tag { get { return "I24"; } }
|
||||
public override string Description { get { return "HyperWorks image format"; } }
|
||||
public override uint Signature { get { return 0x41343249; } } // 'I24A'
|
||||
public override string Tag => "I24";
|
||||
public override string Description => "HyperWorks RGB image format";
|
||||
public override uint Signature => 0x41343249; // 'I24A'
|
||||
|
||||
public I24Format ()
|
||||
{
|
||||
Signatures = new[] { 0x41343249u, 0x20343249u };
|
||||
}
|
||||
|
||||
public override ImageMetaData ReadMetaData (IBinaryStream file)
|
||||
{
|
||||
@ -46,16 +56,17 @@ namespace GameRes.Formats.HyperWorks
|
||||
int bpp = header.ToInt16 (0x10);
|
||||
if (bpp != 24)
|
||||
return null;
|
||||
return new ImageMetaData {
|
||||
return new I24MetaData {
|
||||
Width = header.ToUInt16 (0xC),
|
||||
Height = header.ToUInt16 (0xE),
|
||||
BPP = bpp,
|
||||
Version = header[3],
|
||||
};
|
||||
}
|
||||
|
||||
public override ImageData Read (IBinaryStream file, ImageMetaData info)
|
||||
{
|
||||
var reader = new I24Decoder (file, info);
|
||||
var reader = new I24Decoder (file, (I24MetaData)info);
|
||||
return reader.Unpack();
|
||||
}
|
||||
|
||||
@ -68,9 +79,9 @@ namespace GameRes.Formats.HyperWorks
|
||||
internal class I24Decoder
|
||||
{
|
||||
IBinaryStream m_input;
|
||||
ImageMetaData m_info;
|
||||
I24MetaData m_info;
|
||||
|
||||
public I24Decoder (IBinaryStream input, ImageMetaData info)
|
||||
public I24Decoder (IBinaryStream input, I24MetaData info)
|
||||
{
|
||||
m_input = input;
|
||||
m_info = info;
|
||||
@ -170,7 +181,9 @@ namespace GameRes.Formats.HyperWorks
|
||||
short s2 = shift_table[shift_idx + 1];
|
||||
if (shift_token != 0)
|
||||
{
|
||||
while (shift_token --> 0)
|
||||
if (m_info.Version == 'A')
|
||||
{
|
||||
while (shift_idx > 0)
|
||||
{
|
||||
shift_table[shift_idx] = shift_table[shift_idx - 2];
|
||||
shift_table[shift_idx+1] = shift_table[shift_idx - 1];
|
||||
@ -179,6 +192,14 @@ namespace GameRes.Formats.HyperWorks
|
||||
shift_table[0] = s1;
|
||||
shift_table[1] = s2;
|
||||
}
|
||||
else
|
||||
{
|
||||
shift_table[shift_idx ] = shift_table[shift_idx - 2];
|
||||
shift_table[shift_idx + 1] = shift_table[shift_idx - 1];
|
||||
shift_table[shift_idx - 2] = s1;
|
||||
shift_table[shift_idx - 1] = s2;
|
||||
}
|
||||
}
|
||||
int src = 4 * (x + s1);
|
||||
if (color_token >= 216)
|
||||
{
|
||||
|
@ -30,19 +30,20 @@ using System.IO;
|
||||
using System.Text;
|
||||
using GameRes.Compression;
|
||||
|
||||
// [971205][Azlocks] Isle Mystique
|
||||
// [991001][Inspire] days innocent
|
||||
// [000707][inspire] ambience
|
||||
|
||||
namespace GameRes.Formats.Inspire
|
||||
{
|
||||
internal class IdaEntry : Entry
|
||||
internal class IdaEntry : PackedEntry
|
||||
{
|
||||
public uint Flags;
|
||||
public uint Key;
|
||||
}
|
||||
|
||||
[Export(typeof(ArchiveFormat))]
|
||||
public class PakOpener : ArchiveFormat
|
||||
public class IdaOpener : ArchiveFormat
|
||||
{
|
||||
public override string Tag { get { return "IDA"; } }
|
||||
public override string Description { get { return "Inspire resource archive"; } }
|
||||
@ -50,6 +51,11 @@ namespace GameRes.Formats.Inspire
|
||||
public override bool IsHierarchic { get { return false; } }
|
||||
public override bool CanWrite { get { return false; } }
|
||||
|
||||
public IdaOpener ()
|
||||
{
|
||||
Extensions = new[] { "ida", "mha" };
|
||||
}
|
||||
|
||||
public override ArcFile TryOpen (ArcView file)
|
||||
{
|
||||
int version = file.View.ReadInt32 (4);
|
||||
@ -58,8 +64,9 @@ namespace GameRes.Formats.Inspire
|
||||
using (var index = file.CreateStream())
|
||||
{
|
||||
var dir = new List<Entry>();
|
||||
bool has_packed = false;
|
||||
long index_pos = 8;
|
||||
for (;;)
|
||||
do
|
||||
{
|
||||
index.Position = index_pos;
|
||||
uint entry_length = index.ReadUInt32();
|
||||
@ -76,15 +83,27 @@ namespace GameRes.Formats.Inspire
|
||||
|
||||
var entry = FormatCatalog.Instance.Create<IdaEntry> (name);
|
||||
entry.Offset = offset;
|
||||
entry.Size = size;
|
||||
if (!entry.CheckPlacement (file.MaxOffset))
|
||||
entry.Size = entry.UnpackedSize = size;
|
||||
if (offset > file.MaxOffset || offset < index_pos)
|
||||
return null;
|
||||
entry.IsPacked = (flags & 0x14) != 0;
|
||||
entry.Flags = flags;
|
||||
entry.Key = key;
|
||||
has_packed = has_packed || entry.IsPacked;
|
||||
dir.Add (entry);
|
||||
}
|
||||
while (index_pos < dir[0].Offset);
|
||||
if (0 == dir.Count)
|
||||
return null;
|
||||
if (has_packed) // set proper sizes
|
||||
{
|
||||
long last_offset = file.MaxOffset;
|
||||
for (int i = dir.Count - 1; i >= 0; --i)
|
||||
{
|
||||
dir[i].Size = (uint)(last_offset - dir[i].Offset);
|
||||
last_offset = dir[i].Offset;
|
||||
}
|
||||
}
|
||||
return new ArcFile (file, this, dir);
|
||||
}
|
||||
}
|
||||
|
@ -90,8 +90,11 @@
|
||||
<Compile Include="Asura\ImageMTG.cs" />
|
||||
<Compile Include="BlackButterfly\ArcDAT.cs" />
|
||||
<Compile Include="CottonClub\ImageLMG.cs" />
|
||||
<Compile Include="Gsx\ArcK5.cs" />
|
||||
<Compile Include="Gsx\ImageK4.cs" />
|
||||
<Compile Include="Herb\ArcPAK.cs" />
|
||||
<Compile Include="Herb\ImageGRP.cs" />
|
||||
<Compile Include="HyperWorks\ImageG.cs" />
|
||||
<Compile Include="James\ImageJMG.cs" />
|
||||
<Compile Include="BRoom\ArcCPC.cs" />
|
||||
<Compile Include="BRoom\ArcPK.cs" />
|
||||
@ -125,6 +128,8 @@
|
||||
<Compile Include="Lazycrew\ImageDAT.cs" />
|
||||
<Compile Include="Liddell\ArcFLK.cs" />
|
||||
<Compile Include="Liddell\ImageBPA.cs" />
|
||||
<Compile Include="Logg\ArcARF.cs" />
|
||||
<Compile Include="Logg\ImageFRM.cs" />
|
||||
<Compile Include="Melonpan\ArcTTD.cs" />
|
||||
<Compile Include="Mermaid\AudioPWV.cs" />
|
||||
<Compile Include="Mermaid\ImageGP1.cs" />
|
||||
@ -132,6 +137,7 @@
|
||||
<Compile Include="Mink\ImageFD.cs" />
|
||||
<Compile Include="Mmfass\ArcSDA.cs" />
|
||||
<Compile Include="Nyoken\ArcZLK.cs" />
|
||||
<Compile Include="Omi\ArcDAT.cs" />
|
||||
<Compile Include="Paprika\ArcPKDAT.cs" />
|
||||
<Compile Include="Paprika\ImageNP.cs" />
|
||||
<Compile Include="PenguinWorks\ArcPAC.cs" />
|
||||
@ -145,8 +151,10 @@
|
||||
<Compile Include="PrimeSoft\ImageTHP.cs" />
|
||||
<Compile Include="ProjectMyu\ImageGAM.cs" />
|
||||
<Compile Include="Ransel\ArcBCD.cs" />
|
||||
<Compile Include="Rare\ArcX.cs" />
|
||||
<Compile Include="Regrips\AudioWRG.cs" />
|
||||
<Compile Include="Regrips\ImagePRG.cs" />
|
||||
<Compile Include="Rhss\ArcCRG.cs" />
|
||||
<Compile Include="Rina\ImageRAD.cs" />
|
||||
<Compile Include="RSystem\ArcRAD.cs" />
|
||||
<Compile Include="RSystem\ImageRSG.cs" />
|
||||
|
141
Legacy/Logg/ArcARF.cs
Normal file
141
Legacy/Logg/ArcARF.cs
Normal file
@ -0,0 +1,141 @@
|
||||
//! \file ArcARF.cs
|
||||
//! \date 2023 Sep 03
|
||||
//! \brief Logg resource archive.
|
||||
//
|
||||
// Copyright (C) 2023 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.Collections.Generic;
|
||||
using System.ComponentModel.Composition;
|
||||
using System.IO;
|
||||
|
||||
// [980417][Logg] Tenshi Kourin
|
||||
// [980828][Logg] Kazeiro no Romance
|
||||
|
||||
namespace GameRes.Formats.Logg
|
||||
{
|
||||
[Export(typeof(ArchiveFormat))]
|
||||
public class ArfOpener : ArchiveFormat
|
||||
{
|
||||
public override string Tag => "ARF";
|
||||
public override string Description => "Logg archive file";
|
||||
public override uint Signature => 0;
|
||||
public override bool IsHierarchic => true;
|
||||
public override bool CanWrite => false;
|
||||
|
||||
public override ArcFile TryOpen (ArcView file)
|
||||
{
|
||||
int count = file.View.ReadInt32 (0);
|
||||
if (!IsSaneCount (count))
|
||||
return null;
|
||||
|
||||
uint index = 4;
|
||||
var dir = new List<Entry> (count);
|
||||
for (int i = 0; i < count; ++i)
|
||||
{
|
||||
uint offset = file.View.ReadUInt32 (index);
|
||||
if (offset <= index || offset > file.MaxOffset)
|
||||
return null;
|
||||
uint size = file.View.ReadUInt32 (index+4);
|
||||
byte name_len = file.View.ReadByte (index+8);
|
||||
var name = file.View.ReadString (index+9, name_len);
|
||||
var entry = Create<PackedEntry> (name);
|
||||
entry.Offset = offset;
|
||||
entry.UnpackedSize = size;
|
||||
dir.Add (entry);
|
||||
index += name_len + 9u;
|
||||
if (index > dir[0].Offset)
|
||||
return null;
|
||||
}
|
||||
long last_offset = file.MaxOffset;
|
||||
for (int i = count-1; i >= 0; --i)
|
||||
{
|
||||
var entry = dir[i] as PackedEntry;
|
||||
entry.Size = (uint)(last_offset - entry.Offset);
|
||||
last_offset = entry.Offset;
|
||||
if (string.IsNullOrEmpty (entry.Name))
|
||||
dir.RemoveAt (i);
|
||||
else
|
||||
entry.IsPacked = entry.Size != entry.UnpackedSize;
|
||||
}
|
||||
return new ArcFile (file, this, dir);
|
||||
}
|
||||
|
||||
public override Stream OpenEntry (ArcFile arc, Entry entry)
|
||||
{
|
||||
var pent = (PackedEntry)entry;
|
||||
if (!pent.IsPacked)
|
||||
return base.OpenEntry (arc, entry);
|
||||
var input = arc.File.CreateStream (pent.Offset, pent.Size);
|
||||
var output = new byte[pent.UnpackedSize];
|
||||
Decompress (input, output);
|
||||
return new BinMemoryStream (output, pent.Name);
|
||||
}
|
||||
|
||||
void Decompress (IBinaryStream input, byte[] output)
|
||||
{
|
||||
using (var bits = new LsbBitStream (input.AsStream, true))
|
||||
{
|
||||
int dst = 0;
|
||||
while (dst < output.Length)
|
||||
{
|
||||
if (bits.GetNextBit() == 0)
|
||||
{
|
||||
output[dst++] = (byte)bits.GetBits (8);
|
||||
}
|
||||
else
|
||||
{
|
||||
int count;
|
||||
if (bits.GetNextBit() == 0)
|
||||
count = 2;
|
||||
else if (bits.GetNextBit() == 0)
|
||||
count = 3;
|
||||
else if (bits.GetNextBit() == 0)
|
||||
count = 4;
|
||||
else if (bits.GetNextBit() == 0)
|
||||
count = 5;
|
||||
else
|
||||
{
|
||||
switch (bits.GetBits (2))
|
||||
{
|
||||
case 0: count = 6; break;
|
||||
case 1: count = bits.GetBits (2) + 7; break;
|
||||
case 2: count = bits.GetBits (4) + 11; break;
|
||||
case 3: count = bits.GetBits (10) + 26; break;
|
||||
default: throw new EndOfStreamException();
|
||||
}
|
||||
}
|
||||
int offset;
|
||||
if (bits.GetNextBit() == 0)
|
||||
offset = bits.GetBits (8);
|
||||
else if (bits.GetNextBit() == 0)
|
||||
offset = bits.GetBits (10) + 0x100;
|
||||
else
|
||||
offset = bits.GetBits (12) + 0x500;
|
||||
Binary.CopyOverlapped (output, dst - offset - 1, dst, count);
|
||||
dst += count;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -36,11 +36,11 @@ namespace GameRes.Formats.Logg
|
||||
[Export(typeof(ArchiveFormat))]
|
||||
public class MbmOpener : ArchiveFormat
|
||||
{
|
||||
public override string Tag { get { return "MBM"; } }
|
||||
public override string Description { get { return "Logg Adv engine resource archive"; } }
|
||||
public override uint Signature { get { return 0; } }
|
||||
public override bool IsHierarchic { get { return false; } }
|
||||
public override bool CanWrite { get { return false; } }
|
||||
public override string Tag => "MBM";
|
||||
public override string Description => "Logg Adv engine resource archive";
|
||||
public override uint Signature => 0;
|
||||
public override bool IsHierarchic => false;
|
||||
public override bool CanWrite => false;
|
||||
|
||||
public override ArcFile TryOpen (ArcView file)
|
||||
{
|
||||
@ -63,19 +63,27 @@ namespace GameRes.Formats.Logg
|
||||
|
||||
IDictionary<uint, string> GetArchiveIndex (ArcView file)
|
||||
{
|
||||
uint last_offset = FileListMap.Value.Keys.Last();
|
||||
string list_name;
|
||||
if (!ArcSizeToFileListMap.TryGetValue ((uint)file.MaxOffset, out list_name))
|
||||
return null;
|
||||
var file_map = ReadFileList (list_name);
|
||||
uint last_offset = file_map.Keys.Last();
|
||||
if (last_offset != file.MaxOffset)
|
||||
return null;
|
||||
return FileListMap.Value;
|
||||
return file_map;
|
||||
}
|
||||
|
||||
Lazy<IDictionary<uint, string>> FileListMap = new Lazy<IDictionary<uint, string>> (ReadFileList);
|
||||
static readonly Dictionary<uint, string> ArcSizeToFileListMap = new Dictionary<uint, string> {
|
||||
{ 0x0AB0F5F4, "logg_pl.lst" },
|
||||
{ 0x0BFFD3DA, "logg_ak.lst" },
|
||||
{ 0x09809196, "logg_th.lst" },
|
||||
};
|
||||
|
||||
static IDictionary<uint, string> ReadFileList ()
|
||||
static IDictionary<uint, string> ReadFileList (string list_name)
|
||||
{
|
||||
var file_map = new SortedDictionary<uint,string>();
|
||||
var comma = new char[] {','};
|
||||
FormatCatalog.Instance.ReadFileList ("logg_pl.lst", line => {
|
||||
FormatCatalog.Instance.ReadFileList (list_name, line => {
|
||||
var parts = line.Split (comma, 2);
|
||||
uint offset = uint.Parse (parts[0], NumberStyles.HexNumber);
|
||||
if (2 == parts.Length)
|
||||
|
63
Legacy/Logg/ImageFRM.cs
Normal file
63
Legacy/Logg/ImageFRM.cs
Normal file
@ -0,0 +1,63 @@
|
||||
//! \file ImageFRM.cs
|
||||
//! \date 2023 Sep 03
|
||||
//! \brief Logg image file.
|
||||
//
|
||||
// Copyright (C) 2023 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.ComponentModel.Composition;
|
||||
using System.IO;
|
||||
using System.Windows.Media;
|
||||
|
||||
namespace GameRes.Formats.Logg
|
||||
{
|
||||
[Export(typeof(ImageFormat))]
|
||||
public class FrmFormat : ImageFormat
|
||||
{
|
||||
public override string Tag { get => "FRM"; }
|
||||
public override string Description { get => "Logg image format"; }
|
||||
public override uint Signature { get => 0x4D5246; } // 'FRM'
|
||||
|
||||
public override ImageMetaData ReadMetaData (IBinaryStream file)
|
||||
{
|
||||
var header = file.ReadHeader (0x10);
|
||||
return new ImageMetaData {
|
||||
Width = header.ToUInt32 (4),
|
||||
Height = header.ToUInt32 (8),
|
||||
BPP = 8,
|
||||
};
|
||||
}
|
||||
|
||||
public override ImageData Read (IBinaryStream file, ImageMetaData info)
|
||||
{
|
||||
file.Position = 0x0C;
|
||||
int stride = file.ReadInt32();
|
||||
var palette = ReadPalette (file.AsStream);
|
||||
var pixels = file.ReadBytes (stride * info.iHeight);
|
||||
return ImageData.Create (info, PixelFormats.Indexed8, palette, pixels, stride);
|
||||
}
|
||||
|
||||
public override void Write (Stream file, ImageData image)
|
||||
{
|
||||
throw new System.NotImplementedException ("FrmFormat.Write not implemented");
|
||||
}
|
||||
}
|
||||
}
|
213
Legacy/Omi/ArcDAT.cs
Normal file
213
Legacy/Omi/ArcDAT.cs
Normal file
@ -0,0 +1,213 @@
|
||||
//! \file ArcDAT.cs
|
||||
//! \date 2023 Sep 04
|
||||
//! \brief OMI Script Engine resource archive.
|
||||
//
|
||||
// Copyright (C) 2023 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.Collections.Generic;
|
||||
using System.ComponentModel.Composition;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
|
||||
namespace GameRes.Formats.Omi
|
||||
{
|
||||
[Export(typeof(ArchiveFormat))]
|
||||
public class DatOpener : ArchiveFormat
|
||||
{
|
||||
public override string Tag => "DAT/OMI";
|
||||
public override string Description => "OMI Script Engine resource archive";
|
||||
public override uint Signature => 0;
|
||||
public override bool IsHierarchic => false;
|
||||
public override bool CanWrite => false;
|
||||
|
||||
internal const uint DefaultKey = 7654321u;
|
||||
|
||||
public DatOpener ()
|
||||
{
|
||||
ContainedFormats = new[] { "BMP", "TGA", "WAV", "TXT" };
|
||||
}
|
||||
|
||||
public override ArcFile TryOpen (ArcView file)
|
||||
{
|
||||
if (!VFS.IsPathEqualsToFileName (file.Name, "scrdat"))
|
||||
return null;
|
||||
using (var input = file.CreateStream())
|
||||
using (var index = new DecryptedStream (input, DefaultKey, 0))
|
||||
{
|
||||
var line = index.ReadLine();
|
||||
int count = int.Parse (line);
|
||||
if (!IsSaneCount (count))
|
||||
return null;
|
||||
var dir = new List<Entry> (count);
|
||||
for (int i = 0; i < count; ++i)
|
||||
{
|
||||
var name = index.ReadLine();
|
||||
line = index.ReadLine();
|
||||
uint size = uint.Parse (line);
|
||||
var entry = Create<PackedEntry> (name);
|
||||
entry.Size = size;
|
||||
entry.IsPacked = entry.Type == "image";
|
||||
dir.Add (entry);
|
||||
}
|
||||
long data_pos = index.Position;
|
||||
for (int i = 0; i < count; ++i)
|
||||
{
|
||||
dir[i].Offset = data_pos;
|
||||
if (!dir[i].CheckPlacement (file.MaxOffset))
|
||||
return null;
|
||||
data_pos += dir[i].Size;
|
||||
}
|
||||
return new ArcFile (file, this, dir);
|
||||
}
|
||||
}
|
||||
|
||||
public override Stream OpenEntry (ArcFile arc, Entry entry)
|
||||
{
|
||||
var pent = (PackedEntry)entry;
|
||||
Stream input = arc.File.CreateStream (entry.Offset, entry.Size);
|
||||
input = new DecryptedStream (input, DefaultKey, (uint)entry.Offset);
|
||||
if (!pent.IsPacked)
|
||||
return input;
|
||||
using (var packed = new BinaryStream (input, pent.Name))
|
||||
{
|
||||
var unpacked = DecompressRle (packed);
|
||||
if (pent.UnpackedSize == 0)
|
||||
pent.UnpackedSize = (uint)unpacked.Length;
|
||||
return new BinMemoryStream (unpacked, pent.Name);
|
||||
}
|
||||
}
|
||||
|
||||
internal static byte[] DecompressRle (IBinaryStream input)
|
||||
{
|
||||
int size = input.ReadInt32();
|
||||
var output = new byte[size * 2];
|
||||
ushort rle_marker = input.ReadUInt16();
|
||||
int dst = 0;
|
||||
while (dst < output.Length)
|
||||
{
|
||||
input.Read (output, dst, 2);
|
||||
if (output.ToUInt16 (dst) == rle_marker)
|
||||
{
|
||||
input.Read (output, dst, 2);
|
||||
dst += 2;
|
||||
int count = input.ReadUInt16() - 1;
|
||||
if (count > 0)
|
||||
{
|
||||
count *= 2;
|
||||
Binary.CopyOverlapped (output, dst-2, dst, count);
|
||||
dst += count;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
dst += 2;
|
||||
}
|
||||
}
|
||||
return output;
|
||||
}
|
||||
}
|
||||
|
||||
internal class DecryptedStream : InputProxyStream
|
||||
{
|
||||
private uint m_key;
|
||||
|
||||
static readonly Encoding Encoding = Encodings.cp932;
|
||||
|
||||
public override bool CanSeek { get => false; }
|
||||
public override long Position
|
||||
{
|
||||
get => BaseStream.Position;
|
||||
set => throw new NotSupportedException ("Stream.Position property is not supported");
|
||||
}
|
||||
|
||||
public DecryptedStream (Stream stream, uint key, uint start_offset) : base (stream)
|
||||
{
|
||||
if (start_offset > 0)
|
||||
{
|
||||
do
|
||||
{
|
||||
key = 5 * key - 3;
|
||||
}
|
||||
while (--start_offset > 0);
|
||||
}
|
||||
m_key = key;
|
||||
}
|
||||
|
||||
public override int Read (byte[] buffer, int offset, int count)
|
||||
{
|
||||
int read = BaseStream.Read (buffer, offset, count);
|
||||
Decrypt (buffer, offset, read);
|
||||
return read;
|
||||
}
|
||||
|
||||
byte[] m_byte_buffer = new byte[1];
|
||||
|
||||
public override int ReadByte ()
|
||||
{
|
||||
int b = BaseStream.ReadByte();
|
||||
if (-1 != b)
|
||||
{
|
||||
m_byte_buffer[0] = (byte)b;
|
||||
Decrypt (m_byte_buffer, 0, 1);
|
||||
b = m_byte_buffer[0];
|
||||
}
|
||||
return b;
|
||||
}
|
||||
|
||||
internal void Decrypt (byte[] data, int offset, int count)
|
||||
{
|
||||
for (int i = 0; i < count; ++i)
|
||||
{
|
||||
data[offset+i] = (byte)(Binary.RotByteR (data[offset+i], 1) - m_key);
|
||||
m_key = 5 * m_key - 3;
|
||||
}
|
||||
}
|
||||
|
||||
byte[] m_buffer;
|
||||
|
||||
public string ReadLine ()
|
||||
{
|
||||
if (null == m_buffer)
|
||||
m_buffer = new byte[32];
|
||||
int size = 0;
|
||||
for (;;)
|
||||
{
|
||||
int b = ReadByte();
|
||||
if (-1 == b || '\n' == b)
|
||||
break;
|
||||
if (m_buffer.Length == size)
|
||||
{
|
||||
Array.Resize (ref m_buffer, checked(size/2*3));
|
||||
}
|
||||
m_buffer[size++] = (byte)b;
|
||||
}
|
||||
return Encoding.GetString (m_buffer, 0, size);
|
||||
}
|
||||
|
||||
public override long Seek (long offset, SeekOrigin origin)
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
}
|
||||
}
|
132
Legacy/Rare/ArcX.cs
Normal file
132
Legacy/Rare/ArcX.cs
Normal file
@ -0,0 +1,132 @@
|
||||
//! \file ArcX.cs
|
||||
//! \date 2023 Sep 02
|
||||
//! \brief Rare archive format.
|
||||
//
|
||||
// Copyright (C) 2023 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;
|
||||
|
||||
// [990528][Rare] Seisen Ren'ai Yuugi
|
||||
|
||||
namespace GameRes.Formats.Rare
|
||||
{
|
||||
[Export(typeof(ArchiveFormat))]
|
||||
public class XOpener : ArchiveFormat
|
||||
{
|
||||
public override string Tag => "X/RARE";
|
||||
public override string Description => "Rare resource archive";
|
||||
public override uint Signature => 0;
|
||||
public override bool IsHierarchic => false;
|
||||
public override bool CanWrite => false;
|
||||
|
||||
static readonly Dictionary<string, Tuple<uint, int>> KnownExeMap = new Dictionary<string, Tuple<uint, int>> {
|
||||
{ "seisen.exe", Tuple.Create (0x3A9A0u, 715) },
|
||||
};
|
||||
|
||||
public override ArcFile TryOpen (ArcView file)
|
||||
{
|
||||
if (!VFS.IsPathEqualsToFileName (file.Name, "PP.X"))
|
||||
return null;
|
||||
string full_exe_name = null;
|
||||
Tuple<uint, int> index_pos = null;
|
||||
foreach (var exe_name in KnownExeMap.Keys)
|
||||
{
|
||||
full_exe_name = VFS.ChangeFileName (file.Name, exe_name);
|
||||
if (VFS.FileExists (full_exe_name))
|
||||
{
|
||||
index_pos = KnownExeMap[exe_name];
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (null == index_pos)
|
||||
return null;
|
||||
uint index_offset = index_pos.Item1;
|
||||
int count = index_pos.Item2;
|
||||
using (var index = VFS.OpenView (full_exe_name))
|
||||
{
|
||||
index.View.Reserve (index_offset, (uint)count * 12u);
|
||||
var dir = new List<Entry> (count);
|
||||
for (int i = 0; i < count; ++i)
|
||||
{
|
||||
var entry = new PackedEntry {
|
||||
Name = string.Format ("PP#{0:D5}.BMP", i),
|
||||
Type = "image",
|
||||
Offset = index.View.ReadUInt32 (index_offset),
|
||||
Size = index.View.ReadUInt32 (index_offset+4),
|
||||
UnpackedSize = index.View.ReadUInt32 (index_offset+8),
|
||||
IsPacked = true,
|
||||
};
|
||||
if (!entry.CheckPlacement (file.MaxOffset))
|
||||
return null;
|
||||
dir.Add (entry);
|
||||
index_offset += 12;
|
||||
}
|
||||
return new ArcFile (file, this, dir);
|
||||
}
|
||||
}
|
||||
|
||||
public override Stream OpenEntry (ArcFile arc, Entry entry)
|
||||
{
|
||||
var pent = (PackedEntry)entry;
|
||||
using (var input = arc.File.CreateStream (entry.Offset, entry.Size))
|
||||
{
|
||||
var output = new byte[pent.UnpackedSize];
|
||||
Decompress (input, output);
|
||||
return new BinMemoryStream (output, entry.Name);
|
||||
}
|
||||
}
|
||||
|
||||
internal static void Decompress (IBinaryStream input, byte[] output)
|
||||
{
|
||||
var frame = new byte[0x400];
|
||||
int frame_pos = 1;
|
||||
int dst = 0;
|
||||
using (var bits = new MsbBitStream (input.AsStream, true))
|
||||
{
|
||||
while (dst < output.Length)
|
||||
{
|
||||
int ctl = bits.GetNextBit();
|
||||
if (-1 == ctl)
|
||||
break;
|
||||
if (ctl != 0)
|
||||
{
|
||||
int v = bits.GetBits (8);
|
||||
output[dst++] = frame[frame_pos++ & 0x3FF] = (byte)v;
|
||||
}
|
||||
else
|
||||
{
|
||||
int offset = bits.GetBits (10);
|
||||
int count = bits.GetBits (5) + 2;
|
||||
while (count --> 0)
|
||||
{
|
||||
byte v = frame[offset++ & 0x3FF];
|
||||
output[dst++] = frame[frame_pos++ & 0x3FF] = v;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
83
Legacy/Rhss/ArcCRG.cs
Normal file
83
Legacy/Rhss/ArcCRG.cs
Normal file
@ -0,0 +1,83 @@
|
||||
//! \file ArcCRG.cs
|
||||
//! \date 2023 Aug 28
|
||||
//! \brief Raishū Hyōjun Script System resource archive.
|
||||
//
|
||||
// Copyright (C) 2023 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.Compression;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.Composition;
|
||||
using System.IO;
|
||||
|
||||
namespace GameRes.Formats.Rhss
|
||||
{
|
||||
[Export(typeof(ArchiveFormat))]
|
||||
public class PakOpener : ArchiveFormat
|
||||
{
|
||||
public override string Tag => "DAT/CRG";
|
||||
public override string Description => "RHSS engine resource archive";
|
||||
public override uint Signature => 0x00475243; // 'CRG'
|
||||
public override bool IsHierarchic => false;
|
||||
public override bool CanWrite => false;
|
||||
|
||||
public override ArcFile TryOpen (ArcView file)
|
||||
{
|
||||
int count = file.View.ReadInt32 (4);
|
||||
if (!IsSaneCount (count))
|
||||
return null;
|
||||
uint index_pos = 8;
|
||||
var dir = new List<Entry> (count);
|
||||
for (int i = 0; i < count; ++i)
|
||||
{
|
||||
var name = file.View.ReadString (index_pos+8, 0x30);
|
||||
var entry = Create<PackedEntry> (name);
|
||||
entry.Offset = file.View.ReadUInt32 (index_pos);
|
||||
entry.Size = file.View.ReadUInt32 (index_pos+4);
|
||||
if (!entry.CheckPlacement (file.MaxOffset))
|
||||
return null;
|
||||
dir.Add (entry);
|
||||
index_pos += 60;
|
||||
}
|
||||
return new ArcFile (file, this, dir);
|
||||
}
|
||||
|
||||
public override Stream OpenEntry (ArcFile arc, Entry entry)
|
||||
{
|
||||
var pent = entry as PackedEntry;
|
||||
if (null != pent)
|
||||
{
|
||||
if (!pent.IsPacked && arc.File.View.AsciiEqual (entry.Offset, "CMP\0"))
|
||||
{
|
||||
pent.IsPacked = true;
|
||||
pent.UnpackedSize = arc.File.View.ReadUInt32 (entry.Offset + 0x4C);
|
||||
}
|
||||
if (pent.IsPacked)
|
||||
{
|
||||
Stream input = arc.File.CreateStream (entry.Offset+0x50, entry.Size-0x50);
|
||||
input = new ZLibStream (input, CompressionMode.Decompress);
|
||||
return new XoredStream (input, 0xFF);
|
||||
}
|
||||
}
|
||||
return base.OpenEntry (arc, entry);
|
||||
}
|
||||
}
|
||||
}
|
@ -27,6 +27,9 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.Composition;
|
||||
using System.IO;
|
||||
using System.Windows.Media;
|
||||
|
||||
// [030817][Splush Wave] Knock Out -Taisengata Datsui Mahjong-
|
||||
|
||||
namespace GameRes.Formats.SplushWave
|
||||
{
|
||||
@ -129,5 +132,92 @@ namespace GameRes.Formats.SplushWave
|
||||
}
|
||||
return new BinMemoryStream (output, 0, dst);
|
||||
}
|
||||
|
||||
public override IImageDecoder OpenImage (ArcFile arc, Entry entry)
|
||||
{
|
||||
var fent = (FlkEntry)entry;
|
||||
var input = BinaryStream.FromStream (OpenEntry (arc, fent), fent.Name);
|
||||
if ((fent.Flags & 0x10) == 0)
|
||||
return ImageFormatDecoder.Create (input);
|
||||
try
|
||||
{
|
||||
var info = Swg.ReadMetaData (input) as SwgMetaData;
|
||||
if (null == info)
|
||||
{
|
||||
input.Position = 0;
|
||||
return new ImageFormatDecoder(input);
|
||||
}
|
||||
return new Swg1ImageDecoder (input, info);
|
||||
}
|
||||
catch
|
||||
{
|
||||
input.Dispose();
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
static readonly ResourceInstance<SwgFormat> s_swg = new ResourceInstance<SwgFormat> ("SWG");
|
||||
|
||||
internal static SwgFormat Swg { get => s_swg.Value; }
|
||||
}
|
||||
|
||||
internal sealed class Swg1ImageDecoder : BinaryImageDecoder
|
||||
{
|
||||
SwgMetaData m_info;
|
||||
|
||||
public Swg1ImageDecoder (IBinaryStream input, SwgMetaData info) : base (input, info)
|
||||
{
|
||||
SourceFormat = DatOpener.Swg;
|
||||
m_info = info;
|
||||
}
|
||||
|
||||
static readonly byte[] PlaneMap = { 3, 2, 1, 0 };
|
||||
|
||||
protected override ImageData GetImageData ()
|
||||
{
|
||||
m_input.Position = m_info.DataOffset;
|
||||
int stride = 4 * m_info.iWidth;
|
||||
int plane_size = m_info.iWidth * m_info.iHeight;
|
||||
var output = new byte[stride * m_info.iHeight];
|
||||
ushort[] ctl_buf = new ushort[m_info.iHeight];
|
||||
for (int c = 0; c < 4; ++c)
|
||||
{
|
||||
int compress_method = ReadU16BE();
|
||||
if (0 == compress_method)
|
||||
{
|
||||
int dst = PlaneMap[c] + stride * (m_info.iHeight - 1);
|
||||
for (int y = 0; y < m_info.iHeight; ++y)
|
||||
{
|
||||
for (int x = 0; x < stride; x += 4)
|
||||
{
|
||||
output[dst+x] = m_input.ReadUInt8();
|
||||
}
|
||||
dst -= stride;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (compress_method != 1)
|
||||
throw new InvalidFormatException();
|
||||
for (int y = 0; y < m_info.iHeight; ++y)
|
||||
{
|
||||
ctl_buf[y] = ReadU16BE();
|
||||
}
|
||||
int row = PlaneMap[c];
|
||||
for (int y = 0; y < m_info.iHeight; ++y)
|
||||
{
|
||||
int dst = row;
|
||||
int row_size = ctl_buf[y];
|
||||
SwgFormat.DecompressRow (m_input, row_size, output, dst, 4);
|
||||
row += stride;
|
||||
}
|
||||
}
|
||||
return ImageData.Create (m_info, PixelFormats.Bgra32, null, output, stride);
|
||||
}
|
||||
|
||||
ushort ReadU16BE ()
|
||||
{
|
||||
ushort le = m_input.ReadUInt16();
|
||||
return (ushort)(le >> 8 | le << 8);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -68,7 +68,7 @@ namespace GameRes.Formats.SplushWave
|
||||
{
|
||||
var meta = (SwgMetaData)info;
|
||||
PixelFormat format = meta.BPP == 8 ? PixelFormats.Indexed8
|
||||
: meta.BPP == 32 ? PixelFormats.Bgr32 : PixelFormats.Bgr24;
|
||||
: meta.BPP == 32 ? PixelFormats.Bgra32 : PixelFormats.Bgr24;
|
||||
BitmapPalette palette = null;
|
||||
if (meta.BPP == 8)
|
||||
{
|
||||
@ -77,36 +77,40 @@ namespace GameRes.Formats.SplushWave
|
||||
}
|
||||
int stride = meta.iWidth * meta.BPP / 8;
|
||||
file.Position = meta.DataOffset;
|
||||
// var pixels = new byte[stride * meta.iHeight];
|
||||
var pixels = new byte[4 * meta.iWidth * meta.iHeight];
|
||||
var pixels = new byte[stride * meta.iHeight];
|
||||
if (!meta.IsCompressed)
|
||||
{
|
||||
file.Read (pixels, 0, pixels.Length);
|
||||
return ImageData.CreateFlipped (meta, format, palette, pixels, stride);
|
||||
}
|
||||
var input = file.ReadBytes ((int)(file.Length - file.Position));
|
||||
if (!Decompress (input, pixels, meta.Depth + 2, meta.iWidth, meta.iHeight))
|
||||
if (!Decompress (file, pixels, meta.Depth + 2, meta.iWidth, meta.iHeight))
|
||||
throw new InvalidFormatException ("Invalid SWG file.");
|
||||
return ImageData.CreateFlipped (meta, format, palette, pixels, stride);
|
||||
}
|
||||
|
||||
bool Decompress (byte[] input, byte[] output, int channels, int width, int height)
|
||||
static readonly byte[] PlaneMap = { 2, 1, 0, 3 };
|
||||
|
||||
bool Decompress (IBinaryStream input, byte[] output, int channels, int width, int height)
|
||||
{
|
||||
int src = 0;
|
||||
if (input[0] != 0 || input[1] != 1)
|
||||
long start_pos = input.Position;
|
||||
byte hi = input.ReadUInt8();
|
||||
byte lo = input.ReadUInt8();
|
||||
if (hi != 0 || lo != 1)
|
||||
{
|
||||
input.Position = start_pos;
|
||||
int n = 0;
|
||||
for (int i = 0; i < channels; ++i)
|
||||
{
|
||||
if (0 == input[i])
|
||||
if (0 == input.ReadByte())
|
||||
++n;
|
||||
}
|
||||
if (n != channels)
|
||||
return false;
|
||||
src = 4;
|
||||
input.Position = start_pos + 4;
|
||||
hi = input.ReadUInt8();
|
||||
lo = input.ReadUInt8();
|
||||
}
|
||||
int compress_method = input[src+1] + (input[src] << 8);
|
||||
src += 2;
|
||||
int compress_method = lo | hi << 8;
|
||||
if (0 == compress_method)
|
||||
{
|
||||
for (int i = 0; i < channels; ++i)
|
||||
@ -115,7 +119,7 @@ namespace GameRes.Formats.SplushWave
|
||||
int count = height * width;
|
||||
while (count --> 0)
|
||||
{
|
||||
output[pos] = input[src++];
|
||||
output[pos] = input.ReadUInt8();
|
||||
pos += channels;
|
||||
}
|
||||
}
|
||||
@ -123,61 +127,55 @@ namespace GameRes.Formats.SplushWave
|
||||
}
|
||||
if (compress_method != 1)
|
||||
return false;
|
||||
int dst = 0;
|
||||
int v33 = src;
|
||||
int v37 = height * channels;
|
||||
src += 2 * v37;
|
||||
for (int row = 0; row < v37; ++row)
|
||||
int stride = width * channels;
|
||||
var row_sizes = input.ReadBytes (2 * height * channels);
|
||||
int ctl_pos = 0;
|
||||
for (int c = 0; c < channels; ++c)
|
||||
for (int y = height - 1; y >= 0; --y)
|
||||
{
|
||||
int y = row % height;
|
||||
dst = channels * (width * (height - y - 1) + 1) - row / height - 1;
|
||||
if (dst > output.Length)
|
||||
int dst = stride * y + PlaneMap[c];
|
||||
int row_size = row_sizes[ctl_pos+1] | row_sizes[ctl_pos] << 8;
|
||||
ctl_pos += 2;
|
||||
DecompressRow (input, row_size, output, dst, channels);
|
||||
}
|
||||
return true;
|
||||
int v24 = 0;
|
||||
int v36 = input[v33+1] + (input[v33] << 8);
|
||||
v33 += 2;
|
||||
do
|
||||
}
|
||||
|
||||
internal static void DecompressRow (IBinaryStream input, int row_size, byte[] output, int dst, int step)
|
||||
{
|
||||
byte lo = input[src];
|
||||
byte hi = input[src+1];
|
||||
if (lo != 0)
|
||||
int x = 0;
|
||||
while (x < row_size)
|
||||
{
|
||||
if (lo < 0x81)
|
||||
byte ctl = input.ReadUInt8();
|
||||
if (ctl == 0)
|
||||
{
|
||||
++src;
|
||||
int count = lo + 1;
|
||||
v24 += count + 1;
|
||||
byte v = input.ReadUInt8();
|
||||
x += 2;
|
||||
output[dst] = v;
|
||||
dst += step;
|
||||
}
|
||||
else if (ctl < 0x81u)
|
||||
{
|
||||
int count = ctl + 1;
|
||||
x += count + 1;
|
||||
while (count --> 0)
|
||||
{
|
||||
output[dst] = input[src++];
|
||||
dst += channels;
|
||||
output[dst] = input.ReadUInt8();
|
||||
dst += step;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
src += 2;
|
||||
v24 += 2;
|
||||
int count = Math.Min (0x101 - lo, output.Length - dst);
|
||||
byte v = input.ReadUInt8();
|
||||
x += 2;
|
||||
int count = 0x101 - ctl;
|
||||
while (count --> 0)
|
||||
{
|
||||
output[dst] = hi;
|
||||
dst += channels;
|
||||
output[dst] = v;
|
||||
dst += step;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
src += 2;
|
||||
v24 += 2;
|
||||
output[dst] = hi;
|
||||
dst += channels;
|
||||
}
|
||||
if (dst >= output.Length)
|
||||
return true;
|
||||
}
|
||||
while (v24 < v36);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public override void Write (Stream file, ImageData image)
|
||||
|
Loading…
Reference in New Issue
Block a user