Merge remote-tracking branch 'upstream/master'

This commit is contained in:
Crsky 2022-11-29 18:43:15 +08:00
commit 8b4fc93fbe
67 changed files with 2707 additions and 344 deletions

View File

@ -63,6 +63,7 @@
<Reference Include="System" /> <Reference Include="System" />
<Reference Include="System.Buffers, Version=4.0.3.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL"> <Reference Include="System.Buffers, Version=4.0.3.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
<HintPath>..\packages\System.Buffers.4.5.1\lib\net461\System.Buffers.dll</HintPath> <HintPath>..\packages\System.Buffers.4.5.1\lib\net461\System.Buffers.dll</HintPath>
<HintPath>..\packages\System.Buffers.4.5.1\lib\netstandard1.1\System.Buffers.dll</HintPath>
</Reference> </Reference>
<Reference Include="System.ComponentModel.Composition" /> <Reference Include="System.ComponentModel.Composition" />
<Reference Include="System.Core" /> <Reference Include="System.Core" />
@ -92,6 +93,15 @@
</Reference> </Reference>
<Reference Include="System.ValueTuple, Version=4.0.3.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL"> <Reference Include="System.ValueTuple, Version=4.0.3.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
<HintPath>..\packages\System.ValueTuple.4.5.0\lib\net461\System.ValueTuple.dll</HintPath> <HintPath>..\packages\System.ValueTuple.4.5.0\lib\net461\System.ValueTuple.dll</HintPath>
<Reference Include="System.Memory, Version=4.0.1.1, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
<HintPath>..\packages\System.Memory.4.5.4\lib\netstandard1.1\System.Memory.dll</HintPath>
</Reference>
<Reference Include="System.Numerics" />
<Reference Include="System.Runtime.CompilerServices.Unsafe, Version=4.0.5.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>..\packages\System.Runtime.CompilerServices.Unsafe.4.6.0\lib\netstandard1.0\System.Runtime.CompilerServices.Unsafe.dll</HintPath>
</Reference>
<Reference Include="System.ValueTuple, Version=4.0.3.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
<HintPath>..\packages\System.ValueTuple.4.5.0\lib\netstandard1.0\System.ValueTuple.dll</HintPath>
</Reference> </Reference>
<Reference Include="System.Xaml" /> <Reference Include="System.Xaml" />
<Reference Include="System.Xml.Linq" /> <Reference Include="System.Xml.Linq" />
@ -120,6 +130,8 @@
<Compile Include="AdvSys\ImageGWD.cs" /> <Compile Include="AdvSys\ImageGWD.cs" />
<Compile Include="Ail\ArcLNK2.cs" /> <Compile Include="Ail\ArcLNK2.cs" />
<Compile Include="BlueGale\ImageBBM.cs" /> <Compile Include="BlueGale\ImageBBM.cs" />
<Compile Include="Hypatia\ArcLPK.cs" />
<Compile Include="Hypatia\ImageLSG.cs" />
<Compile Include="JamCreation\ArcDAT.cs" /> <Compile Include="JamCreation\ArcDAT.cs" />
<Compile Include="JamCreation\ImageDPO.cs" /> <Compile Include="JamCreation\ImageDPO.cs" />
<Compile Include="AliceSoft\ArcAAR.cs" /> <Compile Include="AliceSoft\ArcAAR.cs" />
@ -226,6 +238,7 @@
<Compile Include="Ivory\ArcSG.cs" /> <Compile Include="Ivory\ArcSG.cs" />
<Compile Include="Kaguya\ArcPL00.cs" /> <Compile Include="Kaguya\ArcPL00.cs" />
<Compile Include="Kaguya\ArcPL10.cs" /> <Compile Include="Kaguya\ArcPL10.cs" />
<Compile Include="Kaguya\ArcPLT.cs" />
<Compile Include="Key\ArcPAK.cs" /> <Compile Include="Key\ArcPAK.cs" />
<Compile Include="Key\AudioOGGPAK.cs" /> <Compile Include="Key\AudioOGGPAK.cs" />
<Compile Include="Key\ImageCZ.cs" /> <Compile Include="Key\ImageCZ.cs" />
@ -324,6 +337,7 @@
<Compile Include="Qlie\DelphiDeserializer.cs" /> <Compile Include="Qlie\DelphiDeserializer.cs" />
<Compile Include="Qlie\Encryption.cs" /> <Compile Include="Qlie\Encryption.cs" />
<Compile Include="Qlie\ImageABMP.cs" /> <Compile Include="Qlie\ImageABMP.cs" />
<Compile Include="Qlie\ImageARGB.cs" />
<Compile Include="RealLive\ArcKOE.cs" /> <Compile Include="RealLive\ArcKOE.cs" />
<Compile Include="RealLive\ArcSEEN.cs" /> <Compile Include="RealLive\ArcSEEN.cs" />
<Compile Include="RealLive\ImageG00Jpeg.cs" /> <Compile Include="RealLive\ImageG00Jpeg.cs" />
@ -336,6 +350,7 @@
<Compile Include="ScenePlayer\ArcPMX.cs" /> <Compile Include="ScenePlayer\ArcPMX.cs" />
<Compile Include="Scoop\ArcGX.cs" /> <Compile Include="Scoop\ArcGX.cs" />
<Compile Include="Scoop\ImageSCP.cs" /> <Compile Include="Scoop\ImageSCP.cs" />
<Compile Include="Seraphim\ArcArchAngel.cs" />
<Compile Include="Seraphim\ArcCP3.cs" /> <Compile Include="Seraphim\ArcCP3.cs" />
<Compile Include="Seraphim\ArcMC.cs" /> <Compile Include="Seraphim\ArcMC.cs" />
<Compile Include="Seraphim\ArcSCN.cs" /> <Compile Include="Seraphim\ArcSCN.cs" />
@ -652,6 +667,7 @@
<Compile Include="TanukiSoft\ArcTAC.cs" /> <Compile Include="TanukiSoft\ArcTAC.cs" />
<Compile Include="TanukiSoft\ImageAF.cs" /> <Compile Include="TanukiSoft\ImageAF.cs" />
<Compile Include="Taskforce\ArcDAT.cs" /> <Compile Include="Taskforce\ArcDAT.cs" />
<Compile Include="TechnoBrain\ArcIPQ.cs" />
<Compile Include="TechnoBrain\AudioWAPE.cs" /> <Compile Include="TechnoBrain\AudioWAPE.cs" />
<Compile Include="TechnoBrain\ImageIPF.cs" /> <Compile Include="TechnoBrain\ImageIPF.cs" />
<Compile Include="TechnoBrain\ImageIPH.cs" /> <Compile Include="TechnoBrain\ImageIPH.cs" />
@ -792,12 +808,14 @@
<Compile Include="BlackCyc\ArcVPK.cs" /> <Compile Include="BlackCyc\ArcVPK.cs" />
<Compile Include="Unity\ArcASSET.cs" /> <Compile Include="Unity\ArcASSET.cs" />
<Compile Include="Unity\ArcBIN.cs" /> <Compile Include="Unity\ArcBIN.cs" />
<Compile Include="Unity\ArcDSM.cs" />
<Compile Include="Unity\ArcSpVM.cs" /> <Compile Include="Unity\ArcSpVM.cs" />
<Compile Include="Unity\ArcUnityFS.cs" /> <Compile Include="Unity\ArcUnityFS.cs" />
<Compile Include="Unity\Asset.cs" /> <Compile Include="Unity\Asset.cs" />
<Compile Include="Unity\AssetReader.cs" /> <Compile Include="Unity\AssetReader.cs" />
<Compile Include="Unity\AudioClip.cs" /> <Compile Include="Unity\AudioClip.cs" />
<Compile Include="Unity\AudioFSB5.cs" /> <Compile Include="Unity\AudioFSB5.cs" />
<Compile Include="Unity\Bc7Decoder.cs" />
<Compile Include="Unity\BundleStream.cs" /> <Compile Include="Unity\BundleStream.cs" />
<None Include="packages.config" /> <None Include="packages.config" />
<None Include="Unity\Gx4Lib\ArcDAT.cs" /> <None Include="Unity\Gx4Lib\ArcDAT.cs" />

View File

@ -40,13 +40,13 @@ namespace GameRes.Formats
{ {
get get
{ {
return (long)(m_reader.DecodedTime.TotalSeconds * m_reader.SampleRate * m_reader.Channels * sizeof(float)); return (long)(m_reader.TimePosition.TotalSeconds * m_reader.SampleRate * m_reader.Channels * sizeof(float));
} }
set set
{ {
if (value < 0 || value > Length) throw new ArgumentOutOfRangeException("value"); if (value < 0 || value > Length) throw new ArgumentOutOfRangeException("value");
m_reader.DecodedTime = TimeSpan.FromSeconds((double)value / m_reader.SampleRate / m_reader.Channels / sizeof(float)); m_reader.TimePosition = TimeSpan.FromSeconds((double)value / m_reader.SampleRate / m_reader.Channels / sizeof(float));
} }
} }
@ -75,7 +75,7 @@ namespace GameRes.Formats
public override void Reset () public override void Reset ()
{ {
m_reader.DecodedTime = TimeSpan.FromSeconds (0); m_reader.TimePosition = TimeSpan.FromSeconds (0);
} }
// This buffer can be static because it can only be used by 1 instance per thread // This buffer can be static because it can only be used by 1 instance per thread

View File

@ -205,7 +205,10 @@ namespace GameRes.Formats.Cyberworks
string game_name = arc_name != "Arc06.dat" ? TryParseMeta (VFS.CombinePath (dir_name, "Arc06.dat")) : null; string game_name = arc_name != "Arc06.dat" ? TryParseMeta (VFS.CombinePath (dir_name, "Arc06.dat")) : null;
Tuple<string, int> parsed = null; Tuple<string, int> parsed = null;
if (string.IsNullOrEmpty (game_name)) if (string.IsNullOrEmpty (game_name))
{
game_name = TryParseMeta (VFS.CombinePath (dir_name, "Arc00.dat"));
parsed = s_name_parsers.Select (p => p.ParseName (arc_name)).FirstOrDefault (p => p != null); parsed = s_name_parsers.Select (p => p.ParseName (arc_name)).FirstOrDefault (p => p != null);
}
else // Shukujo no Tsuyagoto special case else // Shukujo no Tsuyagoto special case
parsed = OldDatOpener.ArcNameParser.ParseName (arc_name); parsed = OldDatOpener.ArcNameParser.ParseName (arc_name);
if (null == parsed) if (null == parsed)
@ -217,7 +220,7 @@ namespace GameRes.Formats.Cyberworks
var toc = ReadToc (toc_name, 8); var toc = ReadToc (toc_name, 8);
if (null == toc) if (null == toc)
return null; return null;
using (var index = new ArcIndexReader (toc, file, arc_idx)) using (var index = new ArcIndexReader (toc, file, arc_idx, game_name))
{ {
if (!index.Read()) if (!index.Read())
return null; return null;
@ -311,10 +314,8 @@ namespace GameRes.Formats.Cyberworks
if ('c' == type || 'b' == type) if ('c' == type || 'b' == type)
{ {
uint img_size = Binary.BigEndian (input.ReadUInt32()); uint img_size = Binary.BigEndian (input.ReadUInt32());
if (input.Length - 5 == img_size) long start_pos = input.Length - img_size;
{ input = BinaryStream.FromStream (new StreamRegion (input.AsStream, start_pos, img_size), input.Name);
input = BinaryStream.FromStream (new StreamRegion (input.AsStream, 5, img_size), input.Name);
}
} }
else if (scheme != null && ('a' == type || 'd' == type) && input.Length > 21) else if (scheme != null && ('a' == type || 'd' == type) && input.Length > 21)
{ {
@ -618,9 +619,13 @@ namespace GameRes.Formats.Cyberworks
return true; return true;
} }
uint m_fault_id = 100000;
internal PackedEntry ReadEntryInfo () internal PackedEntry ReadEntryInfo ()
{ {
uint id = m_index.ReadUInt32(); uint id = m_index.ReadUInt32();
if (id > m_fault_id)
id = m_fault_id++;
var entry = new PackedEntry { Name = id.ToString ("D6") }; var entry = new PackedEntry { Name = id.ToString ("D6") };
entry.UnpackedSize = m_index.ReadUInt32(); entry.UnpackedSize = m_index.ReadUInt32();
entry.Size = m_index.ReadUInt32(); entry.Size = m_index.ReadUInt32();
@ -650,10 +655,14 @@ namespace GameRes.Formats.Cyberworks
internal class ArcIndexReader : IndexReader internal class ArcIndexReader : IndexReader
{ {
int m_arc_number; int m_arc_number;
string m_game_name;
bool m_ignore_b_files = false;
public ArcIndexReader (byte[] toc, ArcView file, int arc_number) : base (toc, file) public ArcIndexReader (byte[] toc, ArcView file, int arc_number, string game_name = null) : base (toc, file)
{ {
m_arc_number = arc_number; m_arc_number = arc_number;
m_game_name = game_name;
m_ignore_b_files = m_game_name == "ドキドキ母娘レッスン ~教えて♪Hなお勉強~";
} }
char[] m_type = new char[2]; char[] m_type = new char[2];
@ -677,7 +686,7 @@ namespace GameRes.Formats.Cyberworks
ext = new string (m_type); ext = new string (m_type);
else else
ext = new string (m_type[0], 1); ext = new string (m_type[0], 1);
if ("b0" == ext || "n0" == ext || "o0" == ext || "0b" == ext || "b" == ext) if ("b0" == ext || "n0" == ext || "o0" == ext || "0b" == ext || ("b" == ext && !m_ignore_b_files))
{ {
entry.Type = "image"; entry.Type = "image";
HasImages = true; HasImages = true;

View File

@ -2,7 +2,7 @@
//! \date Fri Jun 17 18:49:04 2016 //! \date Fri Jun 17 18:49:04 2016
//! \brief Tinker Bell encrypted image file. //! \brief Tinker Bell encrypted image file.
// //
// Copyright (C) 2016-2017 by morkt // Copyright (C) 2016-2022 by morkt
// //
// Permission is hereby granted, free of charge, to any person obtaining a copy // Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to // of this software and associated documentation files (the "Software"), to
@ -28,6 +28,7 @@ using System.Collections.Generic;
using System.IO; using System.IO;
using System.Windows.Media; using System.Windows.Media;
using System.Windows.Media.Imaging; using System.Windows.Media.Imaging;
using GameRes.Utility;
namespace GameRes.Formats.Cyberworks namespace GameRes.Formats.Cyberworks
{ {
@ -145,9 +146,17 @@ namespace GameRes.Formats.Cyberworks
{ {
var size_buf = new byte[4]; var size_buf = new byte[4];
input.Read (size_buf, 0 , 4); input.Read (size_buf, 0 , 4);
var decoder = new PngBitmapDecoder (input, BitmapCreateOptions.None, int png_size = BigEndian.ToInt32 (size_buf, 0);
BitmapCacheOption.OnLoad); BitmapSource frame;
BitmapSource frame = decoder.Frames[0]; // work-around for possible extra padding before PNG data
using (var membuf = new MemoryStream (png_size+4))
{
input.CopyTo (membuf);
membuf.Seek (-png_size, SeekOrigin.End);
var decoder = new PngBitmapDecoder (membuf, BitmapCreateOptions.None,
BitmapCacheOption.OnLoad);
frame = decoder.Frames[0];
}
Info.Width = (uint)frame.PixelWidth; Info.Width = (uint)frame.PixelWidth;
Info.Height = (uint)frame.PixelHeight; Info.Height = (uint)frame.PixelHeight;
if (frame.Format.BitsPerPixel != 32) if (frame.Format.BitsPerPixel != 32)

View File

@ -1,4 +1,4 @@
// Copyright (C) 2019 by morkt // Copyright (C) 2022 by morkt
// //
// Permission is hereby granted, free of charge, to any person obtaining a copy // Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to // of this software and associated documentation files (the "Software"), to

View File

@ -1,4 +1,4 @@
// Copyright (C) 2019 by morkt // Copyright (C) 2022 by morkt
// //
// Permission is hereby granted, free of charge, to any person obtaining a copy // Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to // of this software and associated documentation files (the "Software"), to

View File

@ -1,4 +1,4 @@
// Copyright (C) 2019 by morkt // Copyright (C) 2022 by morkt
// //
// Permission is hereby granted, free of charge, to any person obtaining a copy // Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to // of this software and associated documentation files (the "Software"), to

View File

@ -0,0 +1,61 @@
//! \file ArcLPC.cs
//! \date 2019 Jan 15
//! \brief Kogado Stduio multi-frame image.
//
// Copyright (C) 2019 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.Kogado
{
[Export(typeof(ArchiveFormat))]
public class LpcOpener : ArchiveFormat
{
public override string Tag { get { return "LPC"; } }
public override string Description { get { return "Kogado Studio multi-frame image"; } }
public override uint Signature { get { return 0; } }
public override bool IsHierarchic { get { return false; } }
public override bool CanWrite { get { return false; } }
public override ArcFile TryOpen (ArcView file)
{
if (!file.Name.HasExtension ("LPC"))
return null;
int count = file.View.ReadInt32 (4);
if (!IsSaneCount (count))
return null;
var dir = new List<Entry> (count);
for (int i = 0; i < count; ++i)
{
var entry = Create<Entry> (name);
if (!entry.CheckPlacement (file.MaxOffset))
return null;
dir.Add (entry);
}
return new ArcFile (file, this, dir);
}
}
}

View File

@ -0,0 +1,78 @@
//! \file ArcLPK.cs
//! \date 2022 Apr 12
//! \brief Kogado resource archive.
//
// Copyright (C) 2022 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.Kogado
{
[Export(typeof(ArchiveFormat))]
public class LpkOpener : ArchiveFormat
{
public override string Tag { get { return "LPK/KOGADO"; } }
public override string Description { get { return "Kogado 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 ArcFile TryOpen (ArcView file)
{
if (!file.Name.HasExtension (".lpk") || file.MaxOffset < 0x2800)
return null;
uint index_offset = 0;
var dir = new List<Entry> ();
for (int i = 0; i < 0x200; ++i)
{
if (file.View.ReadByte (index_offset) == 0)
break;
var name = file.View.ReadString (index_offset, 0x10);
if (!IsValidEntryName (name))
return null;
index_offset += 0x10;
var entry = Create<Entry> (name);
dir.Add (entry);
}
if (0 == dir.Count)
return null;
index_offset = 0x2000;
uint base_offset = 0x2800;
uint offset = file.View.ReadUInt32 (index_offset);
foreach (var entry in dir)
{
index_offset += 4;
uint next_offset = file.View.ReadUInt32 (index_offset);
uint size = next_offset - offset;
entry.Offset = offset + base_offset;
entry.Size = size;
if (!entry.CheckPlacement (file.MaxOffset))
return null;
offset = next_offset;
}
return new ArcFile (file, this, dir);
}
}
}

View File

@ -0,0 +1,100 @@
//! \file ImageLSG.cs
//! \date 2022 Apr 12
//! \brief Kogado image format.
//
// Copyright (C) 2022 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.Kogado
{
internal class LsgMetaData : ImageMetaData
{
public int BitmapSize;
}
[Export(typeof(ImageFormat))]
public class LsgFormat : ImageFormat
{
public override string Tag { get { return "LSG"; } }
public override string Description { get { return "Kogado image format"; } }
public override uint Signature { get { return 0x4D42; } } // 'BM'
public override ImageMetaData ReadMetaData (IBinaryStream file)
{
var header = file.ReadHeader (0x14);
var info = new LsgMetaData {
Width = header.ToUInt32 (0x0C),
Height = header.ToUInt32 (0x10),
BPP = header.ToInt32 (8),
BitmapSize = header.ToInt32 (4),
};
if (info.BPP != 8 && info.BPP != 24)
return null;
return info;
}
static readonly string DefaultPaletteName = "base.pal";
public override ImageData Read (IBinaryStream file, ImageMetaData info)
{
var meta = (LsgMetaData)info;
file.Position = 0x14;
var pixels = file.ReadBytes (meta.BitmapSize);
PixelFormat format;
BitmapPalette palette = null;
if (meta.BPP == 8)
{
format = PixelFormats.Indexed8;
palette = ReadDefaultPalette (file.Name);
if (null == palette)
format = PixelFormats.Gray8;
}
else
{
format = PixelFormats.Bgr24;
}
return ImageData.Create (info, format, palette, pixels);
}
public override void Write (Stream file, ImageData image)
{
throw new System.NotImplementedException ("LsgFormat.Write not implemented");
}
internal BitmapPalette ReadDefaultPalette (string filename)
{
var pal_name = Path.ChangeExtension (filename, ".pal");
if (!VFS.FileExists (pal_name))
pal_name = VFS.ChangeFileName (filename, DefaultPaletteName);
if (!VFS.FileExists (pal_name))
return null;
using (var input = VFS.OpenStream (pal_name))
{
return ReadPalette (input, 0x100, PaletteFormat.Rgb);
}
}
}
}

View File

@ -23,7 +23,6 @@
// IN THE SOFTWARE. // IN THE SOFTWARE.
// //
using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.ComponentModel.Composition; using System.ComponentModel.Composition;
using System.IO; using System.IO;
@ -42,60 +41,110 @@ namespace GameRes.Formats.Kaguya
} }
} }
[Export(typeof(ArchiveFormat))] internal class AnmEntry : Entry
public class AnmOpener : ArchiveFormat {
public long ImageDataOffset;
public uint ImageDataSize;
}
internal interface IAnmReader
{
List<Entry> GetFramesList (IBinaryStream input);
}
public abstract class AnmOpenerBase : ArchiveFormat, IAnmReader
{ {
public override string Tag { get { return "ANM/KAGUYA"; } }
public override string Description { get { return "KaGuYa script engine animation resource"; } } public override string Description { get { return "KaGuYa script engine animation resource"; } }
public override uint Signature { get { return 0x30304E41; } } // 'AN00'
public override bool IsHierarchic { get { return false; } } public override bool IsHierarchic { get { return false; } }
public override bool CanWrite { get { return false; } } public override bool CanWrite { get { return false; } }
public AnmOpener () public AnmOpenerBase ()
{ {
Extensions = new string[] { "anm" }; Extensions = new string[] { "anm" };
} }
public override ArcFile TryOpen (ArcView file) public override ArcFile TryOpen (ArcView file)
{ {
int frame_count = file.View.ReadInt16 (0x14); using (var input = file.CreateStream())
uint current_offset = 0x18 + (uint)frame_count * 4;
int count = file.View.ReadInt16 (current_offset);
if (!IsSaneCount (count))
return null;
var base_info = new ImageMetaData
{ {
OffsetX = file.View.ReadInt32 (4), var dir = GetFramesList (input);
OffsetY = file.View.ReadInt32 (8), if (null == dir)
Width = file.View.ReadUInt32 (0x0C), return null;
Height = file.View.ReadUInt32 (0x10), var base_info = GetBaseInfo (input);
BPP = 32, string base_name = Path.GetFileNameWithoutExtension (file.Name);
}; int i = 0;
current_offset += 2; foreach (var entry in dir)
string base_name = Path.GetFileNameWithoutExtension (file.Name);
var dir = new List<Entry> (count);
for (int i = 0; i < count; ++i)
{
uint width = file.View.ReadUInt32 (current_offset+8);
uint height = file.View.ReadUInt32 (current_offset+12);
var entry = new Entry
{ {
Name = string.Format ("{0}#{1:D2}", base_name, i), entry.Name = string.Format ("{0}#{1:D2}", base_name, i++);
Type = "image", entry.Type = "image";
Offset = current_offset, }
Size = 0x10 + 4*width*height, return new AnmArchive (file, this, dir, base_info);
};
dir.Add (entry);
current_offset += entry.Size;
} }
return new AnmArchive (file, this, dir, base_info);
} }
public override IImageDecoder OpenImage (ArcFile arc, Entry entry) public override IImageDecoder OpenImage (ArcFile arc, Entry entry)
{ {
var base_info = ((AnmArchive)arc).ImageInfo; var base_info = ((AnmArchive)arc).ImageInfo;
var input = arc.File.CreateStream (entry.Offset, entry.Size); var input = arc.File.CreateStream (entry.Offset, entry.Size);
return new An00Decoder (input, base_info); return CreateDecoder (input, base_info);
}
internal virtual ImageMetaData GetBaseInfo (IBinaryStream input)
{
input.Position = 4;
return new ImageMetaData
{
OffsetX = input.ReadInt32(),
OffsetY = input.ReadInt32(),
Width = input.ReadUInt32(),
Height = input.ReadUInt32(),
BPP = 32,
};
}
public abstract List<Entry> GetFramesList (IBinaryStream input);
public abstract IImageDecoder CreateDecoder (IBinaryStream input, ImageMetaData info);
}
[Export(typeof(ArchiveFormat))]
public class AnmOpener : AnmOpenerBase
{
public override string Tag { get { return "ANM/KAGUYA"; } }
public override uint Signature { get { return 0x30304E41; } } // 'AN00'
public override List<Entry> GetFramesList (IBinaryStream file)
{
file.Position = 0x14;
int frame_count = file.ReadInt16();
file.Position = 0x18 + frame_count * 4;
int count = file.ReadInt16();
if (!IsSaneCount (count))
return null;
var current_offset = file.Position;
var dir = new List<Entry> (count);
for (int i = 0; i < count; ++i)
{
file.Position = current_offset + 8;
uint width = file.ReadUInt32();
uint height = file.ReadUInt32();
uint image_size = 4*width*height;
var entry = new AnmEntry
{
Offset = current_offset,
Size = 0x10 + image_size,
ImageDataOffset = current_offset + 0x10,
ImageDataSize = image_size,
};
dir.Add (entry);
current_offset += entry.Size;
}
return dir;
}
public override IImageDecoder CreateDecoder (IBinaryStream input, ImageMetaData info)
{
return new An00Decoder (input, info);
} }
} }
@ -123,75 +172,146 @@ namespace GameRes.Formats.Kaguya
} }
[Export(typeof(ArchiveFormat))] [Export(typeof(ArchiveFormat))]
public class An20Opener : ArchiveFormat public class An10Opener : AnmOpenerBase, IAnmReader
{ {
public override string Tag { get { return "AN20/KAGUYA"; } } public override string Tag { get { return "AN10/KAGUYA"; } }
public override string Description { get { return "KaGuYa script engine animation resource"; } } public override uint Signature { get { return 0x30314E41; } } // 'AN10'
public override uint Signature { get { return 0x30324E41; } } // 'AN20'
public override bool IsHierarchic { get { return false; } }
public override bool CanWrite { get { return false; } }
public An20Opener () public override List<Entry> GetFramesList (IBinaryStream file)
{ {
Extensions = new string[] { "anm" }; file.Position = 0x14;
} int frame_count = file.ReadInt16();
file.Position = 0x18 + frame_count * 4;
public override ArcFile TryOpen (ArcView file) int count = file.ReadInt16();
{
int table_count = file.View.ReadInt16 (4);
uint current_offset = 8;
for (int i = 0; i < table_count; ++i)
{
switch (file.View.ReadByte (current_offset++))
{
case 0: break;
case 1: current_offset += 8; break;
case 2:
case 3:
case 4:
case 5: current_offset += 4; break;
default: return null;
}
}
current_offset += 2 + file.View.ReadUInt16 (current_offset) * 8u;
int count = file.View.ReadInt16 (current_offset);
if (!IsSaneCount (count)) if (!IsSaneCount (count))
return null; return null;
current_offset += 2; var current_offset = file.Position;
var base_info = new ImageMetaData
{
OffsetX = file.View.ReadInt32 (current_offset),
OffsetY = file.View.ReadInt32 (current_offset+4),
Width = file.View.ReadUInt32 (current_offset+8),
Height = file.View.ReadUInt32 (current_offset+12),
BPP = 32,
};
current_offset += 0x10;
string base_name = Path.GetFileNameWithoutExtension (file.Name);
var dir = new List<Entry> (count); var dir = new List<Entry> (count);
for (int i = 0; i < count; ++i) for (int i = 0; i < count; ++i)
{ {
uint width = file.View.ReadUInt32 (current_offset+8); file.Position = current_offset + 8;
uint height = file.View.ReadUInt32 (current_offset+0x0C); uint width = file.ReadUInt32();
uint depth = file.View.ReadUInt32 (current_offset+0x10); uint height = file.ReadUInt32();
var entry = new Entry uint channels = file.ReadUInt32();
uint image_size = channels*width*height;
var entry = new AnmEntry
{ {
Name = string.Format ("{0}#{1:D2}", base_name, i),
Type = "image",
Offset = current_offset, Offset = current_offset,
Size = 0x14 + depth*width*height, Size = 0x14 + image_size,
ImageDataOffset = current_offset + 0x14,
ImageDataSize = image_size,
}; };
dir.Add (entry); dir.Add (entry);
current_offset += entry.Size; current_offset += entry.Size;
} }
return new AnmArchive (file, this, dir, base_info); return dir;
} }
public override IImageDecoder OpenImage (ArcFile arc, Entry entry) public override IImageDecoder CreateDecoder (IBinaryStream input, ImageMetaData info)
{ {
var base_info = ((AnmArchive)arc).ImageInfo; return new An10Decoder (input, info);
var input = arc.File.CreateStream (entry.Offset, entry.Size); }
return new An20Decoder (input, base_info); }
internal class An10Decoder : BinaryImageDecoder
{
public An10Decoder (IBinaryStream input, ImageMetaData base_info) : base (input)
{
Info = new ImageMetaData
{
OffsetX = base_info.OffsetX + m_input.ReadInt32(),
OffsetY = base_info.OffsetY + m_input.ReadInt32(),
Width = m_input.ReadUInt32(),
Height = m_input.ReadUInt32(),
BPP = m_input.ReadInt32() * 8,
};
}
protected override ImageData GetImageData ()
{
m_input.Position = 0x14;
int stride = Info.BPP / 8 * Info.iWidth;
var pixels = m_input.ReadBytes (stride*Info.iHeight);
PixelFormat format = 24 == Info.BPP ? PixelFormats.Bgr24 : PixelFormats.Bgra32;
return ImageData.CreateFlipped (Info, format, null, pixels, stride);
}
}
[Export(typeof(ArchiveFormat))]
public class An20Opener : AnmOpenerBase
{
public override string Tag { get { return "AN20/KAGUYA"; } }
public override uint Signature { get { return 0x30324E41; } } // 'AN20'
public override List<Entry> GetFramesList (IBinaryStream file)
{
if (!SkipFrameTable (file))
return null;
int count = file.ReadInt16();
if (!IsSaneCount (count))
return null;
long current_offset = file.Position + 0x10;
var dir = new List<Entry> (count);
for (int i = 0; i < count; ++i)
{
file.Position = current_offset + 8;
uint width = file.ReadUInt32();
uint height = file.ReadUInt32();
uint depth = file.ReadUInt32();
uint image_size = depth*width*height;
var entry = new AnmEntry
{
Offset = current_offset,
Size = 0x14 + image_size,
ImageDataOffset = current_offset + 0x14,
ImageDataSize = image_size,
};
dir.Add (entry);
current_offset += entry.Size;
}
return dir;
}
internal override ImageMetaData GetBaseInfo (IBinaryStream input)
{
SkipFrameTable (input);
input.ReadInt16();
return new ImageMetaData
{
OffsetX = input.ReadInt32(),
OffsetY = input.ReadInt32(),
Width = input.ReadUInt32(),
Height = input.ReadUInt32(),
BPP = 32,
};
}
bool SkipFrameTable (IBinaryStream file)
{
file.Position = 4;
int table_count = file.ReadInt16();
file.Position = 8;
for (int i = 0; i < table_count; ++i)
{
switch (file.ReadByte())
{
case 0: break;
case 1: file.Seek (8, SeekOrigin.Current); break;
case 2:
case 3:
case 4:
case 5: file.Seek (4, SeekOrigin.Current); break;
default: return false;
}
}
int count = file.ReadUInt16();
file.Seek (count * 8, SeekOrigin.Current);
return true;
}
public override IImageDecoder CreateDecoder (IBinaryStream input, ImageMetaData info)
{
return new An20Decoder (input, info);
} }
} }

View File

@ -90,7 +90,7 @@ namespace GameRes.Formats.Kaguya
} }
} }
byte[] UnpackLzss (IBinaryStream input, uint unpacked_size) internal static byte[] UnpackLzss (IBinaryStream input, uint unpacked_size)
{ {
var output = new byte[unpacked_size]; var output = new byte[unpacked_size];
var frame = new byte[0x100]; var frame = new byte[0x100];

View File

@ -65,7 +65,7 @@ namespace GameRes.Formats.Kaguya
{ {
int version = file.View.ReadByte (4) - '0'; int version = file.View.ReadByte (4) - '0';
if (version < 3 || version > 6) if (version < 3 || version > 6)
return null; return ReadOldIndex (file);
using (var reader = LinkReader.Create (file, version)) using (var reader = LinkReader.Create (file, version))
{ {
@ -87,7 +87,22 @@ namespace GameRes.Formats.Kaguya
{ {
var lent = entry as LinkEntry; var lent = entry as LinkEntry;
if (null == lent || (!lent.IsPacked && !lent.IsEncrypted)) if (null == lent || (!lent.IsPacked && !lent.IsEncrypted))
{
if (entry.Size > 8)
{
uint unpacked_size = arc.File.View.ReadUInt32 (entry.Offset);
int id = arc.File.View.ReadUInt16 (entry.Offset+5);
if (id == 0x4D42) // 'BM'
{
using (var input = arc.File.CreateStream (entry.Offset+4, entry.Size-4, entry.Name))
{
var data = Lin2Opener.UnpackLzss (input, unpacked_size);
return new BinMemoryStream (data, entry.Name);
}
}
}
return base.OpenEntry (arc, entry); return base.OpenEntry (arc, entry);
}
if (lent.IsEncrypted) if (lent.IsEncrypted)
{ {
var larc = arc as LinkArchive; var larc = arc as LinkArchive;
@ -102,6 +117,35 @@ namespace GameRes.Formats.Kaguya
return new BinMemoryStream (bmr.Data, entry.Name); return new BinMemoryStream (bmr.Data, entry.Name);
} }
} }
internal ArcFile ReadOldIndex (ArcView file)
{
int count = file.View.ReadInt32 (4);
if (!IsSaneCount (count))
return null;
var dir = new List<Entry> (count);
using (var index = file.CreateStream())
{
index.Position = 8;
uint names_size = index.ReadUInt32();
for (int i = 0; i < count; ++i)
{
var name = index.ReadCString();
var entry = FormatCatalog.Instance.Create<Entry> (name);
dir.Add (entry);
}
index.Position = 12 + names_size;
foreach (var entry in dir)
{
entry.Offset = index.ReadUInt32();
entry.Size = index.ReadUInt32();
if (!entry.CheckPlacement (file.MaxOffset))
return null;
}
}
return new ArcFile (file, this, dir);
}
} }
internal class LinkReader : IDisposable internal class LinkReader : IDisposable
@ -440,9 +484,15 @@ namespace GameRes.Formats.Kaguya
var header = input.ReadHeader (0x11); var header = input.ReadHeader (0x11);
if (header.AsciiEqual ("[SCR-PARAMS]v0")) if (header.AsciiEqual ("[SCR-PARAMS]v0"))
{ {
var version = Version.Parse (header.GetCString (13, 4)); Version version;
if ('.' == header[15])
version = Version.Parse (header.GetCString (13, 4));
else
version = new Version (header[14] - '0', 0);
if (2 == version.Major) if (2 == version.Major)
return new ParamsV2Deserializer (input, version); return new ParamsV2Deserializer (input, version);
else if (version.Major < 5)
return new ParamsV4Deserializer (input, version);
else if (5 == version.Major && (version.Minor >= 4 && version.Minor <= 7)) else if (5 == version.Major && (version.Minor >= 4 && version.Minor <= 7))
return new ParamsV5Deserializer (input, version); return new ParamsV5Deserializer (input, version);
} }
@ -499,6 +549,22 @@ namespace GameRes.Formats.Kaguya
SkipString(); SkipString();
} }
} }
protected void ReadHeader (int start)
{
m_input.Position = start;
SkipChunk();
m_title = ReadString();
if (m_version.Major < 3)
m_input.ReadCString();
SkipString();
SkipString();
m_input.ReadByte();
SkipString();
SkipString();
SkipDict();
m_input.ReadByte();
}
} }
internal class ParamsV2Deserializer : ParamsDeserializer internal class ParamsV2Deserializer : ParamsDeserializer
@ -515,17 +581,7 @@ namespace GameRes.Formats.Kaguya
public override byte[] GetKey () public override byte[] GetKey ()
{ {
m_input.Position = 0x17; ReadHeader (0x17);
SkipChunk();
m_title = ReadString();
m_input.ReadCString();
SkipString();
SkipString();
m_input.ReadByte();
SkipString();
SkipString();
SkipDict();
m_input.ReadByte();
if ("幼なじみと甘~くエッチに過ごす方法" == m_title) if ("幼なじみと甘~くエッチに過ごす方法" == m_title)
{ {
@ -565,6 +621,37 @@ namespace GameRes.Formats.Kaguya
} }
} }
internal class ParamsV4Deserializer : ParamsDeserializer
{
public ParamsV4Deserializer (IBinaryStream input, Version version) : base (input, version)
{
}
public override byte[] GetKey ()
{
ReadHeader (0x19);
Skip (m_version.Major < 5 ? 12 : 11);
int count = m_input.ReadUInt8();
for (int i = 0; i < count; ++i)
{
m_input.ReadByte();
SkipChunk();
SkipArray();
SkipArray();
}
SkipDict();
count = m_input.ReadUInt8();
for (int i = 0; i < count; ++i)
{
SkipChunk();
SkipArray();
SkipArray();
}
return ReadKey();
}
}
internal class ParamsV5Deserializer : ParamsDeserializer internal class ParamsV5Deserializer : ParamsDeserializer
{ {
public ParamsV5Deserializer (IBinaryStream input, Version version) : base (input, version) public ParamsV5Deserializer (IBinaryStream input, Version version) : base (input, version)
@ -573,18 +660,9 @@ namespace GameRes.Formats.Kaguya
public override byte[] GetKey () public override byte[] GetKey ()
{ {
// ハラミタマ ReadHeader (0x1B);
m_input.Position = 0x1B;
SkipChunk();
m_title = ReadString();
SkipString();
SkipString();
m_input.ReadByte();
SkipString();
SkipString();
SkipDict();
Skip (m_version.Minor <= 4 ? 0x10 : 0x11); Skip (m_version.Minor <= 4 ? 15 : 16);
for (int i = 0; i < 3; ++i) for (int i = 0; i < 3; ++i)
{ {
if (0 != m_input.ReadUInt8()) if (0 != m_input.ReadUInt8())
@ -633,6 +711,11 @@ namespace GameRes.Formats.Kaguya
delegate Stream Decryptor (LinkArchive arc, LinkEntry entry); delegate Stream Decryptor (LinkArchive arc, LinkEntry entry);
static readonly ResourceInstance<AnmOpener> An00 = new ResourceInstance<AnmOpener> ("ANM/KAGUYA");
static readonly ResourceInstance<An10Opener> An10 = new ResourceInstance<An10Opener> ("AN10/KAGUYA");
static readonly ResourceInstance<An20Opener> An20 = new ResourceInstance<An20Opener> ("AN20/KAGUYA");
static readonly ResourceInstance<Pl00Opener> Pl00 = new ResourceInstance<Pl00Opener> ("PLT/KAGUYA");
public LinkEncryption (byte[] key, bool anm_encrypted = true) public LinkEncryption (byte[] key, bool anm_encrypted = true)
{ {
if (null == key || 0 == key.Length) if (null == key || 0 == key.Length)
@ -675,13 +758,12 @@ namespace GameRes.Formats.Kaguya
return new PrefixStream (header, body); return new PrefixStream (header, body);
} }
Stream DecryptAn00 (LinkArchive arc, LinkEntry entry) Stream DecryptAnm (LinkArchive arc, LinkEntry entry, IAnmReader reader)
{ {
var data = arc.File.View.ReadBytes (entry.Offset, entry.Size); var data = arc.File.View.ReadBytes (entry.Offset, entry.Size);
int frame_offset = 0x18 + data.ToUInt16 (0x14) * 4; var input = new BinMemoryStream (data, entry.Name);
int count = data.ToUInt16 (frame_offset); var dir = reader.GetFramesList (input);
frame_offset += 10; if (dir != null)
for (int i = 0; i < count; ++i)
{ {
int w = data.ToInt32 (frame_offset); int w = data.ToInt32 (frame_offset);
int h = data.ToInt32 (frame_offset+4); int h = data.ToInt32 (frame_offset+4);
@ -691,8 +773,8 @@ namespace GameRes.Formats.Kaguya
frame_offset += size + 8; frame_offset += size + 8;
} }
return new BinMemoryStream (data, entry.Name); return new BinMemoryStream (data, entry.Name);
} }
Stream DecryptAn20(LinkArchive arc, LinkEntry entry) Stream DecryptAn20(LinkArchive arc, LinkEntry entry)
{ {
var data = arc.File.View.ReadBytes(entry.Offset, entry.Size); var data = arc.File.View.ReadBytes(entry.Offset, entry.Size);
@ -701,13 +783,13 @@ namespace GameRes.Formats.Kaguya
for (int i = 0; i < count; ++i) for (int i = 0; i < count; ++i)
{ {
switch (data[offset++]) switch (data[offset++])
{ {
case 0: break; case 0: break;
case 1: offset += 8; break; case 1: offset += 8; break;
case 2: case 2:
case 3: case 3:
case 4: case 4:
case 5: offset += 4; break; case 5: offset += 4; break;
default: return new BinMemoryStream(data, entry.Name); default: return new BinMemoryStream(data, entry.Name);
} }
} }
@ -715,16 +797,16 @@ namespace GameRes.Formats.Kaguya
offset += 2 + count * 8; offset += 2 + count * 8;
int frame_count = data.ToInt16(offset); int frame_count = data.ToInt16(offset);
offset += 18; offset += 18;
for(int i = 0; i < frame_count; ++i) for(int i = 0; i < frame_count; ++i)
{ {
offset += 8; offset += 8;
int w = data.ToInt32(offset); int w = data.ToInt32(offset);
int h = data.ToInt32(offset + 4); int h = data.ToInt32(offset + 4);
int channels = data.ToInt32(offset + 8); int channels = data.ToInt32(offset + 8);
int frame_size = channels * w * h; int frame_size = channels * w * h;
offset += 12; offset += 12;
DecryptData(data, offset, frame_size); DecryptData(data, offset, frame_size);
offset += frame_size; offset += frame_size;
} }
return new BinMemoryStream(data, entry.Name); return new BinMemoryStream(data, entry.Name);
} }
@ -755,27 +837,27 @@ namespace GameRes.Formats.Kaguya
offset += 12; offset += 12;
DecryptData (data, offset, channels * w * h); DecryptData (data, offset, channels * w * h);
return new BinMemoryStream (data, entry.Name); return new BinMemoryStream (data, entry.Name);
} }
Stream DecryptPL00(LinkArchive arc, LinkEntry entry) Stream DecryptPL00(LinkArchive arc, LinkEntry entry)
{ {
var data = arc.File.View.ReadBytes(entry.Offset, entry.Size); var data = arc.File.View.ReadBytes(entry.Offset, entry.Size);
int count = data.ToUInt16(4); int count = data.ToUInt16(4);
int offset = 22; int offset = 22;
for(int i = 0; i < count; ++i) for(int i = 0; i < count; ++i)
{ {
offset += 8; offset += 8;
int w = data.ToInt32(offset); int w = data.ToInt32(offset);
int h = data.ToInt32(offset + 4); int h = data.ToInt32(offset + 4);
int channels = data.ToInt32(offset + 8); int channels = data.ToInt32(offset + 8);
int size = channels * w * h; int size = channels * w * h;
offset += 12; offset += 12;
DecryptData(data, offset, size); DecryptData(data, offset, size);
offset += size; offset += size;
} }
return new BinMemoryStream(data, entry.Name); return new BinMemoryStream(data, entry.Name);
} }
Stream DecryptPL10(LinkArchive arc, LinkEntry entry) Stream DecryptPL10(LinkArchive arc, LinkEntry entry)
{ {
var data = arc.File.View.ReadBytes(entry.Offset, entry.Size); var data = arc.File.View.ReadBytes(entry.Offset, entry.Size);

101
ArcFormats/Kaguya/ArcPLT.cs Normal file
View File

@ -0,0 +1,101 @@
//! \file ArcPLT.cs
//! \date 2022 May 03
//! \brief KaGuYa script engine animation resource.
//
// Copyright (C) 2022 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.Windows.Media;
namespace GameRes.Formats.Kaguya
{
[Export(typeof(ArchiveFormat))]
public class Pl00Opener : AnmOpenerBase
{
public override string Tag { get { return "PLT/KAGUYA"; } }
public override uint Signature { get { return 0x30304C50; } } // 'PL00'
public Pl00Opener ()
{
Extensions = new string[] { "plt" };
}
public override List<Entry> GetFramesList (IBinaryStream file)
{
file.Position = 4;
int count = file.ReadInt16();
if (!IsSaneCount (count))
return null;
file.Position = 0x16;
var current_offset = file.Position;
var dir = new List<Entry> (count);
for (int i = 0; i < count; ++i)
{
file.Position = current_offset + 8;
uint width = file.ReadUInt32();
uint height = file.ReadUInt32();
uint depth = file.ReadUInt32();
uint image_size = depth*width*height;
var entry = new AnmEntry
{
Offset = current_offset,
Size = 0x14 + image_size,
ImageDataOffset = current_offset + 0x14,
ImageDataSize = image_size,
};
dir.Add (entry);
current_offset += entry.Size;
}
return dir;
}
public override IImageDecoder CreateDecoder (IBinaryStream input, ImageMetaData info)
{
return new Pl00Decoder (input, info);
}
}
internal class Pl00Decoder : BinaryImageDecoder
{
public Pl00Decoder (IBinaryStream input, ImageMetaData base_info) : base (input)
{
Info = new ImageMetaData
{
OffsetX = base_info.OffsetX + m_input.ReadInt32(),
OffsetY = base_info.OffsetY + m_input.ReadInt32(),
Width = m_input.ReadUInt32(),
Height = m_input.ReadUInt32(),
BPP = m_input.ReadInt32() * 8,
};
}
protected override ImageData GetImageData ()
{
m_input.Position = 0x14;
int stride = Info.BPP * Info.iWidth / 8;
var pixels = m_input.ReadBytes (stride*Info.iHeight);
PixelFormat format = 24 == Info.BPP ? PixelFormats.Bgr24 : PixelFormats.Bgra32;
return ImageData.CreateFlipped (Info, format, null, pixels, stride);
}
}
}

View File

@ -39,38 +39,62 @@ namespace GameRes.Formats.Liar
public override string Tag { get { return "XFL"; } } public override string Tag { get { return "XFL"; } }
public override string Description { get { return Strings.arcStrings.XFLDescription; } } public override string Description { get { return Strings.arcStrings.XFLDescription; } }
public override uint Signature { get { return 0x0001424c; } } public override uint Signature { get { return 0x0001424c; } }
public override bool IsHierarchic { get { return false; } } public override bool IsHierarchic { get { return true; } }
public override bool CanWrite { get { return true; } } public override bool CanWrite { get { return true; } }
public override ArcFile TryOpen (ArcView file) public override ArcFile TryOpen (ArcView file)
{ {
uint dir_size = file.View.ReadUInt32 (4); var dir = ReadDirectory (file, 0, file.MaxOffset, "");
int count = file.View.ReadInt32 (8); if (dir != null)
if (count <= 0) return new ArcFile (file, this, dir);
else
return null; return null;
long max_offset = file.MaxOffset; }
uint base_offset = dir_size + 12;
if (dir_size >= max_offset || base_offset >= max_offset) internal List<Entry> ReadDirectory (ArcView file, long base_offset, long max_offset, string base_dir)
{
uint dir_size = file.View.ReadUInt32 (base_offset+4);
int count = file.View.ReadInt32 (base_offset+8);
if (!IsSaneCount (count))
return null;
long data_offset = base_offset + dir_size + 12;
if (dir_size >= max_offset || data_offset >= max_offset)
return null; return null;
file.View.Reserve (0, base_offset); file.View.Reserve (base_offset, (uint)(data_offset - base_offset));
long cur_offset = 12; long cur_offset = base_offset + 12;
var dir = new List<Entry> (count); var dir = new List<Entry> (count);
for (int i = 0; i < count; ++i) for (int i = 0; i < count; ++i)
{ {
if (cur_offset+40 > base_offset) if (cur_offset+40 > data_offset)
return null; return null;
string name = file.View.ReadString (cur_offset, 32); string name = file.View.ReadString (cur_offset, 32);
var entry = FormatCatalog.Instance.Create<Entry> (name); var entry_offset = data_offset + file.View.ReadUInt32 (cur_offset+32);
entry.Offset = base_offset + file.View.ReadUInt32 (cur_offset+32); var entry_size = file.View.ReadUInt32 (cur_offset+36);
entry.Size = file.View.ReadUInt32 (cur_offset+36); List<Entry> subdir = null;
if (!entry.CheckPlacement (max_offset)) name = VFS.CombinePath (base_dir, name);
return null; if (name.HasExtension (".xfl") && file.View.ReadUInt32 (entry_offset) == Signature)
dir.Add (entry); {
subdir = ReadDirectory (file, entry_offset, entry_offset + entry_size, name);
}
if (subdir != null && subdir.Count > 0)
{
dir.AddRange (subdir);
}
else
{
var entry = FormatCatalog.Instance.Create<Entry> (name);
entry.Offset = entry_offset;
entry.Size = entry_size;
if (!entry.CheckPlacement (max_offset))
return null;
dir.Add (entry);
}
cur_offset += 40; cur_offset += 40;
} }
return new ArcFile (file, this, dir); return dir;
} }
public override void Create (Stream output, IEnumerable<Entry> list, ResourceOptions options, public override void Create (Stream output, IEnumerable<Entry> list, ResourceOptions options,

View File

@ -61,9 +61,9 @@ namespace GameRes.Formats.Maika
public Mk2Opener () public Mk2Opener ()
{ {
// 'MK2.0' 'BL2.0'. 'SL1.0', 'LS2.0', 'AR2.0' // 'MK2.0' 'BL2.0'. 'SL1.0', 'LS2.0', 'AR2.0', 'MP2.0'
Signatures = new uint[] { Signatures = new uint[] {
0x2E324B4D, 0x2E324C42, 0x2E314C53, 0x2E32534C, 0x2E325241 0x2E324B4D, 0x2E324C42, 0x2E314C53, 0x2E32534C, 0x2E325241, 0x2E32504D
}; };
} }

View File

@ -32,5 +32,5 @@ using System.Runtime.InteropServices;
// You can specify all the values or you can default the Build and Revision Numbers // You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below: // by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")] // [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion ("1.2.48.2153")] [assembly: AssemblyVersion ("1.2.48.2176")]
[assembly: AssemblyFileVersion ("1.2.48.2153")] [assembly: AssemblyFileVersion ("1.2.48.2176")]

View File

@ -84,7 +84,7 @@ namespace GameRes.Formats.Qlie
public PackOpener () public PackOpener ()
{ {
Extensions = new string [] { "pack" }; Extensions = new string [] { "pack" };
ContainedFormats = new[] { "ABMP/QLIE", "DPNG", "PNG", "JPEG", "OGG", "WAV" }; ContainedFormats = new[] { "ABMP/QLIE", "DPNG", "ARGB", "PNG", "JPEG", "OGG", "WAV" };
} }
/// <summary> /// <summary>

View File

@ -0,0 +1,114 @@
//! \file ImageARGB.cs
//! \date 2022 Apr 22
//! \brief QLIE image format.
//
// Copyright (C) 2022 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.Qlie
{
internal class ArgbMetaData : ImageMetaData
{
public uint ImageOffset;
public uint ImageLength;
public uint MaskLength;
}
[Export(typeof(ImageFormat))]
public class ArgbFormat : ImageFormat
{
public override string Tag { get { return "ARGB"; } }
public override string Description { get { return "QLIE image format"; } }
public override uint Signature { get { return 0x42475241; } } // 'ARGB'
public override ImageMetaData ReadMetaData (IBinaryStream file)
{
var header = file.ReadHeader (0x19);
if (!header.AsciiEqual ("ARGBSaveData1\0") || header[0x10] != 3)
return null;
const uint image_offset = 0x19;
uint image_size = header.ToUInt32 (0x11);
uint mask_size = header.ToUInt32 (0x15);
using (var jpeg = OpenStreamRegion (file, image_offset, image_size))
{
var info = Jpeg.ReadMetaData (jpeg);
if (null == info)
return null;
return new ArgbMetaData {
Width = info.Width,
Height = info.Height,
BPP = 32,
ImageOffset = image_offset,
ImageLength = image_size,
MaskLength = mask_size,
};
}
}
internal IBinaryStream OpenStreamRegion (IBinaryStream file, long offset, uint length)
{
var input = new StreamRegion (file.AsStream, offset, length, true);
return new BinaryStream (input, file.Name);
}
public override ImageData Read (IBinaryStream file, ImageMetaData info)
{
var meta = (ArgbMetaData)info;
file.Position = meta.ImageOffset;
var jpeg = new JpegBitmapDecoder (file.AsStream, BitmapCreateOptions.None, BitmapCacheOption.OnLoad);
BitmapSource bitmap = jpeg.Frames[0];
file.Position = meta.ImageOffset + meta.ImageLength;
var png = new PngBitmapDecoder (file.AsStream, BitmapCreateOptions.None, BitmapCacheOption.OnLoad);
BitmapSource mask = png.Frames[0];
if (mask.PixelWidth != bitmap.PixelWidth || mask.PixelHeight != bitmap.PixelHeight)
throw new InvalidFormatException ("ARGB bitmap and mask dimensions mismatch");
if (bitmap.Format.BitsPerPixel != 32)
bitmap = new FormatConvertedBitmap (bitmap, PixelFormats.Bgr32, null, 0);
int stride = bitmap.PixelWidth * 4;
var pixels = new byte[stride * bitmap.PixelHeight];
bitmap.CopyPixels (pixels, stride, 0);
if (mask.Format.BitsPerPixel != 8)
mask = new FormatConvertedBitmap (mask, PixelFormats.Gray8, null, 0);
var alpha = new byte[mask.PixelWidth * mask.PixelHeight];
mask.CopyPixels (alpha, mask.PixelWidth, 0);
int src = 0;
for (int dst = 3; dst < pixels.Length; dst += 4)
{
pixels[dst] = alpha[src++];
}
return ImageData.Create (info, PixelFormats.Bgra32, null, pixels);
}
public override void Write (Stream file, ImageData image)
{
throw new System.NotImplementedException ("ArgbFormat.Write not implemented");
}
}
}

View File

@ -0,0 +1,132 @@
//! \file ArcArchAngel.cs
//! \date 2022 May 01
//! \brief ArchAngel engine resource archive.
//
// Copyright (C) 2022 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.ArchAngel
{
[Export(typeof(ArchiveFormat))]
public class DatOpener : ArchiveFormat
{
public override string Tag { get { return "DAT/ARCH"; } }
public override string Description { get { return "ArchAngel 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 DatOpener ()
{
ContainedFormats = new[] { "CB" };
}
static readonly string[] DefaultSections = { "image", "script", null };
public override ArcFile TryOpen (ArcView file)
{
if (file.MaxOffset > uint.MaxValue
|| !VFS.IsPathEqualsToFileName (file.Name, "ARCHPAC.DAT"))
return null;
int file_count = file.View.ReadInt16 (0);
if (!IsSaneCount (file_count))
return null;
uint index_pos = 2;
var size_table = new uint[file_count];
for (int i = 0; i < file_count; ++i)
{
size_table[i] = file.View.ReadUInt32 (index_pos);
index_pos += 4;
}
var section_table = new SortedDictionary<int, uint>();
uint min_offset = (uint)file.MaxOffset;
while (index_pos + 6 <= min_offset)
{
uint offset = file.View.ReadUInt32 (index_pos);
int index = file.View.ReadInt16 (index_pos+4);
if (index < 0 || index > file_count || offset > file.MaxOffset)
return null;
if (offset < min_offset)
min_offset = offset;
section_table[index] = offset;
index_pos += 6;
}
var dir = new List<Entry> (file_count);
int section_num = 0;
foreach (var section in section_table)
{
int i = section.Key;
uint base_offset = section.Value;
do
{
uint size = size_table[i];
var entry = new PackedEntry {
Name = string.Format ("{0}-{1:D6}", section_num, i),
Offset = base_offset,
Size = size,
};
if (!entry.CheckPlacement (file.MaxOffset))
return null;
if (section_num < DefaultSections.Length && DefaultSections[section_num] != null)
entry.Type = DefaultSections[section_num];
if ("script" == entry.Type)
entry.IsPacked = true;
dir.Add (entry);
base_offset += size;
++i;
}
while (i < file_count && !section_table.ContainsKey (i));
++section_num;
}
return new ArcFile (file, this, dir);
}
public override Stream OpenEntry (ArcFile arc, Entry entry)
{
if (0 == entry.Size)
return Stream.Null;
var pent = entry as PackedEntry;
if (null == pent || !pent.IsPacked || pent.Size <= 4)
return base.OpenEntry (arc, entry);
var input = arc.File.CreateStream (entry.Offset, entry.Size);
if (0 == pent.UnpackedSize)
pent.UnpackedSize = input.Signature;
try
{
var data = Seraphim.ScnOpener.LzDecompress (input);
return new BinMemoryStream (data, entry.Name);
}
catch
{
return arc.File.CreateStream (entry.Offset, entry.Size);
}
finally
{
input.Dispose();
}
}
}
}

View File

@ -112,7 +112,7 @@ namespace GameRes.Formats.Seraphim
} }
} }
internal byte[] LzDecompress (IBinaryStream input) internal static byte[] LzDecompress (IBinaryStream input)
{ {
int unpacked_size = input.ReadInt32(); int unpacked_size = input.ReadInt32();
var data = new byte[unpacked_size]; var data = new byte[unpacked_size];

View File

@ -82,6 +82,7 @@ namespace GameRes.Formats.Seraphim
public ArchPacOpener () public ArchPacOpener ()
{ {
Extensions = new string[] { "dat" }; Extensions = new string[] { "dat" };
ContainedFormats = new[] { "CB" };
} }
public override ArcFile TryOpen (ArcView file) public override ArcFile TryOpen (ArcView file)

View File

@ -124,6 +124,7 @@ namespace GameRes.Formats.Seraphim
{ {
// common case for 256-colors images // common case for 256-colors images
Signatures = new uint[] { 0x01004243, 0 }; Signatures = new uint[] { 0x01004243, 0 };
Extensions = new string[] { "CB", "CLB" };
} }
public override ImageMetaData ReadMetaData (IBinaryStream stream) public override ImageMetaData ReadMetaData (IBinaryStream stream)
@ -133,18 +134,18 @@ namespace GameRes.Formats.Seraphim
return null; return null;
int colors = header.ToUInt16 (2); int colors = header.ToUInt16 (2);
int packed_size = header.ToInt32 (12); int packed_size = header.ToInt32 (12);
if (packed_size <= 0 || packed_size > stream.Length-0x10) if (packed_size <= 0 /*|| packed_size > stream.Length-0x10*/)
return null; return null;
uint width = header.ToUInt16 (8); int width = header.ToInt16 (8);
uint height = header.ToUInt16 (10); int height = header.ToInt16 (10);
if (0 == width || 0 == height) if (width <= 0 || height <= 0 || colors > 0x100)
return null; return null;
return new SeraphMetaData return new SeraphMetaData
{ {
OffsetX = header.ToInt16 (4), OffsetX = header.ToInt16 (4),
OffsetY = header.ToInt16 (6), OffsetY = header.ToInt16 (6),
Width = width, Width = (uint)width,
Height = height, Height = (uint)height,
BPP = 8, BPP = 8,
PackedSize = packed_size, PackedSize = packed_size,
Colors = colors, Colors = colors,
@ -363,7 +364,7 @@ namespace GameRes.Formats.Seraphim
private byte[] UnpackBytes () // sub_403ED0 private byte[] UnpackBytes () // sub_403ED0
{ {
int total = m_width * m_height; int total = m_width * m_height;
var output = new byte[total]; var output = new byte[total + m_width];
int dst = 0; int dst = 0;
while ( dst < total ) while ( dst < total )
{ {
@ -450,9 +451,9 @@ namespace GameRes.Formats.Seraphim
} }
else else
{ {
int v36 = m_input.ReadByte() | ((next & 0xF) << 8); int offset = m_input.ReadByte() | ((next & 0xF) << 8);
count = m_input.ReadByte() + 1; count = m_input.ReadByte() + 1;
int src = dst - 1 - v36; int src = dst - 1 - offset;
Binary.CopyOverlapped (output, src, dst, count); Binary.CopyOverlapped (output, src, dst, count);
} }
dst += count; dst += count;

View File

@ -0,0 +1,116 @@
//! \file ArcIPQ.cs
//! \date 2022 May 02
//! \brief TechnoBrain's animation resource.
//
// Copyright (C) 2022 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.TechnoBrain
{
internal class IpqArchive : ArcFile
{
public readonly IpfMetaData Info;
public IpqArchive (ArcView arc, ArchiveFormat impl, ICollection<Entry> dir, IpfMetaData info)
: base (arc, impl, dir)
{
Info = info;
}
}
[Export(typeof(ArchiveFormat))]
public class IpqOpener : ArchiveFormat
{
public override string Tag { get { return "IPQ"; } }
public override string Description { get { return "TechnoBrain's animation resource"; } }
public override uint Signature { get { return 0; } } // 'RIFF'
public override bool IsHierarchic { get { return false; } }
public override bool CanWrite { get { return false; } }
static readonly ResourceInstance<IpfFormat> Ipf = new ResourceInstance<IpfFormat>("IPF");
public override ArcFile TryOpen (ArcView file)
{
if (file.View.ReadUInt32 (0) != 0x46464952 // 'RIFF'
|| !file.View.AsciiEqual (8, "IPQ fmt "))
return null;
IpfMetaData ipq_info;
using (var ipq = file.CreateStream())
{
ipq_info = Ipf.Value.ReadIpfHeader (ipq);
if (null == ipq_info || ipq_info.FormatString != "IPQ fmt ")
return null;
ipq.Position = ipq_info.DataOffset;
if (ipq.ReadUInt32() != 0x6D696E61) // "anim"
return null;
uint index_size = ipq.ReadUInt32();
int count = ipq.ReadInt32();
if (!IsSaneCount (count))
return null;
var base_name = Path.GetFileNameWithoutExtension (file.Name);
var dir = new List<Entry> (count);
for (int i = 0; i < count; ++i)
{
var entry = new Entry {
Name = string.Format ("{0}#{1:D3}", base_name, i),
Type = "image",
Offset = ipq.ReadUInt32(),
};
dir.Add (entry);
}
long last_offset = file.MaxOffset;
for (int i = count-1; i >= 0; --i)
{
dir[i].Size = (uint)(last_offset - dir[i].Offset);
last_offset = dir[i].Offset;
}
return new IpqArchive (file, this, dir, ipq_info);
}
}
public override IImageDecoder OpenImage (ArcFile arc, Entry entry)
{
var ipq = arc as IpqArchive;
if (null == ipq)
return base.OpenImage (arc, entry);
var info = ipq.Info.Clone() as IpfMetaData;
var file = arc.File.CreateStream();
try
{
file.Position = entry.Offset;
if (!Ipf.Value.ReadBmpInfo (file, info))
throw new InvalidFormatException ("Invalid 'bmp' section.");
return new IpfReader (file, info, Ipf.Value);
}
catch
{
file.Dispose();
throw;
}
}
}
}

View File

@ -23,6 +23,7 @@
// IN THE SOFTWARE. // IN THE SOFTWARE.
// //
using System;
using System.ComponentModel.Composition; using System.ComponentModel.Composition;
using System.IO; using System.IO;
using System.Windows.Media; using System.Windows.Media;
@ -31,13 +32,21 @@ using GameRes.Utility;
namespace GameRes.Formats.TechnoBrain namespace GameRes.Formats.TechnoBrain
{ {
internal class IpfMetaData : ImageMetaData internal class IpfMetaData : ImageMetaData, ICloneable
{ {
public bool HasPalette; public bool HasPalette;
public bool HasBitmap;
public bool IsCompressed; public bool IsCompressed;
public long PalOffset; public long PalOffset;
public int PalSize; public int PalSize;
public long BmpOffset; public long BmpOffset;
public long DataOffset;
public string FormatString;
public object Clone ()
{
return MemberwiseClone();
}
} }
[Export(typeof(ImageFormat))] [Export(typeof(ImageFormat))]
@ -47,24 +56,25 @@ namespace GameRes.Formats.TechnoBrain
public override string Description { get { return "TechnoBrain's 'Inteligent Picture Format'"; } } public override string Description { get { return "TechnoBrain's 'Inteligent Picture Format'"; } }
public override uint Signature { get { return 0; } } // 'RIFF' public override uint Signature { get { return 0; } } // 'RIFF'
public override ImageMetaData ReadMetaData (IBinaryStream file) internal IpfMetaData ReadIpfHeader (IBinaryStream file)
{ {
// 'RIFF' isn't included into signature to avoid auto-detection of the WAV files as IPF images. // 'RIFF' isn't included into signature to avoid auto-detection of the WAV files as IPF images.
if (0x46464952 != file.Signature) // 'RIFF' if (0x46464952 != file.Signature) // 'RIFF'
return null; return null;
var header = file.ReadHeader (0x14); var header = file.ReadHeader (0x14);
if (!header.AsciiEqual (8, "IPF fmt ")) if (!header.AsciiEqual (0xC, "fmt "))
return null; return null;
int fmt_size = header.ToInt32 (0x10); int fmt_size = header.ToInt32 (0x10);
if (fmt_size < 0x24) if (fmt_size < 0x24)
return null; return null;
header = file.ReadHeader (0x14 + fmt_size); header = file.ReadHeader (0x14 + fmt_size);
bool has_palette = header.ToInt32 (0x18) != 0; var info = new IpfMetaData {
bool has_bitmap = header.ToInt32 (0x28) != 0; BPP = 8,
if (!has_bitmap) HasPalette = header.ToInt32 (0x18) != 0,
return null; HasBitmap = header.ToInt32 (0x28) != 0,
var info = new IpfMetaData { BPP = 8, HasPalette = has_palette }; FormatString = header.GetCString (8, 8),
if (has_palette) };
if (info.HasPalette)
{ {
if (0x206C6170 != file.ReadInt32()) // 'pal ' if (0x206C6170 != file.ReadInt32()) // 'pal '
return null; return null;
@ -74,24 +84,43 @@ namespace GameRes.Formats.TechnoBrain
info.PalOffset = file.Position; info.PalOffset = file.Position;
file.Position = info.PalOffset + info.PalSize; file.Position = info.PalOffset + info.PalSize;
} }
info.DataOffset = file.Position;
return info;
}
internal bool ReadBmpInfo (IBinaryStream file, IpfMetaData info)
{
if (0x20706D62 != file.ReadInt32()) // 'bmp ' if (0x20706D62 != file.ReadInt32()) // 'bmp '
return null; return false;
int bmp_size = file.ReadInt32(); int bmp_size = file.ReadInt32();
if (bmp_size <= 0x20) if (bmp_size <= 0x20)
return null; return false;
info.BmpOffset = file.Position + 0x18; info.BmpOffset = file.Position + 0x18;
info.Width = file.ReadUInt16(); info.Width = file.ReadUInt16();
info.Height = file.ReadUInt16(); info.Height = file.ReadUInt16();
file.Seek (0xE, SeekOrigin.Current); file.ReadUInt32();
info.OffsetX = file.ReadInt16();
info.OffsetY = file.ReadInt16();
file.Seek (6, SeekOrigin.Current);
info.IsCompressed = 0 != (file.ReadByte() & 1); info.IsCompressed = 0 != (file.ReadByte() & 1);
return true;
}
public override ImageMetaData ReadMetaData (IBinaryStream file)
{
var info = ReadIpfHeader (file);
if (null == info || info.FormatString != "IPF fmt " || !info.HasBitmap)
return null;
file.Position = info.DataOffset;
if (!ReadBmpInfo (file, info))
return null;
return info; return info;
} }
public override ImageData Read (IBinaryStream file, ImageMetaData info) public override ImageData Read (IBinaryStream file, ImageMetaData info)
{ {
var reader = new IpfReader (file, (IpfMetaData)info); var reader = new IpfReader (file, (IpfMetaData)info, this);
var pixels = reader.Unpack(); return reader.Image;
return ImageData.Create (info, reader.Format, reader.Palette, pixels);
} }
public override void Write (Stream file, ImageData image) public override void Write (Stream file, ImageData image)
@ -100,22 +129,34 @@ namespace GameRes.Formats.TechnoBrain
} }
} }
internal sealed class IpfReader internal sealed class IpfReader : IImageDecoder
{ {
IBinaryStream m_input; IBinaryStream m_input;
IpfMetaData m_info; IpfMetaData m_info;
byte[] m_output; byte[] m_output;
ImageData m_image;
public BitmapPalette Palette { get; private set; } public BitmapPalette Palette { get; private set; }
public PixelFormat Format { get; private set; } public PixelFormat Format { get; private set; }
public byte[] Data { get { return m_output; } }
public IpfReader (IBinaryStream input, IpfMetaData info) public Stream Source { get { return m_input.AsStream; } }
public ImageMetaData Info { get { return m_info; } }
public ImageData Image { get { return m_image ?? (m_image = GetImageData()); } }
public ImageFormat SourceFormat { get; private set; }
public IpfReader (IBinaryStream input, IpfMetaData info, ImageFormat impl)
{ {
m_input = input; m_input = input;
m_info = info; m_info = info;
m_output = new byte[m_info.Width*m_info.Height]; m_output = new byte[m_info.Width*m_info.Height];
Format = m_info.HasPalette ? PixelFormats.Indexed8 : PixelFormats.Gray8; Format = m_info.HasPalette ? PixelFormats.Indexed8 : PixelFormats.Gray8;
SourceFormat = impl;
}
private ImageData GetImageData ()
{
var pixels = Unpack();
return ImageData.Create (m_info, Format, Palette, pixels);
} }
public byte[] Unpack () public byte[] Unpack ()
@ -151,7 +192,7 @@ namespace GameRes.Formats.TechnoBrain
for (int j = 0; j < 8; ++j) for (int j = 0; j < 8; ++j)
{ {
int dst = (i << 3) + j; int dst = (i << 3) + j;
if (dst >= 0x0A && 0 != (bits & 0x80)) if (dst >= min_index && 0 != (bits & 0x80))
{ {
if (dst >= min_index && dst <= max_index) if (dst >= min_index && dst <= max_index)
{ {
@ -199,5 +240,18 @@ namespace GameRes.Formats.TechnoBrain
} }
} }
} }
#region IDisposable members
bool m_disposed = false;
public void Dispose ()
{
if (!m_disposed)
{
m_input.Dispose();
m_disposed = true;
}
GC.SuppressFinalize (this);
}
#endregion
} }
} }

View File

@ -42,12 +42,19 @@ namespace GameRes.Formats.Unity
public override ArcFile TryOpen (ArcView file) public override ArcFile TryOpen (ArcView file)
{ {
uint header_size = Binary.BigEndian (file.View.ReadUInt32 (0)); uint header_size = Binary.BigEndian (file.View.ReadUInt32 (0));
uint file_size = Binary.BigEndian (file.View.ReadUInt32 (4)); long file_size = Binary.BigEndian (file.View.ReadUInt32 (4));
if (file_size != file.MaxOffset || header_size > file_size || 0 == header_size)
return null;
int format = Binary.BigEndian (file.View.ReadInt32 (8)); int format = Binary.BigEndian (file.View.ReadInt32 (8));
uint data_offset = Binary.BigEndian (file.View.ReadUInt32 (12)); if (format <= 0 || format > 0x100)
if (format <= 0 || format > 0x100 || data_offset >= file_size || data_offset < header_size) return null;
long data_offset = Binary.BigEndian (file.View.ReadUInt32 (12));
if (format >= 22)
{
header_size = Binary.BigEndian (file.View.ReadUInt32 (0x14));
file_size = Binary.BigEndian (file.View.ReadInt64 (0x18));
data_offset = Binary.BigEndian (file.View.ReadInt64 (0x20));
}
if (file_size != file.MaxOffset || header_size > file_size || 0 == header_size
|| data_offset >= file_size || data_offset < header_size)
return null; return null;
using (var stream = file.CreateStream()) using (var stream = file.CreateStream())
using (var input = new AssetReader (stream)) using (var input = new AssetReader (stream))
@ -84,7 +91,7 @@ namespace GameRes.Formats.Unity
{ {
reader.SetupReaders (obj.Asset); reader.SetupReaders (obj.Asset);
var tex = new Texture2D(); var tex = new Texture2D();
tex.Load (reader, obj.Asset.Tree.Version); tex.Load (reader, obj.Asset.Tree);
if (0 == tex.m_DataLength) if (0 == tex.m_DataLength)
{ {
reader.Dispose(); reader.Dispose();

View File

@ -0,0 +1,98 @@
//! \file ArcDSM.cs
//! \date 2022 Apr 30
//! \brief Encrypted DSM script file as an archive.
//
// Copyright (C) 2022 by morkt
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//
using System;
using System.Collections.Generic;
using System.ComponentModel.Composition;
using System.IO;
using System.Security.Cryptography;
using System.Text;
namespace GameRes.Formats.Unity
{
[Export(typeof(ArchiveFormat))]
public class DsmOpener : ArchiveFormat
{
public override string Tag { get { return "DSM/UNITY"; } }
public override string Description { get { return "Unity engine encrypted script file"; } }
public override uint Signature { get { return 0; } }
public override bool IsHierarchic { get { return false; } }
public override bool CanWrite { get { return false; } }
const string DefaultPassword = "pass";
public override ArcFile TryOpen (ArcView file)
{
if (!VFS.IsPathEqualsToFileName (file.Name, "data.dsm")
|| (file.View.ReadUInt32 (0) & 0xFFFFFF) != 0xBFBBEF) // UTF-8 BOM
return null;
var dir = new List<Entry> {
new Entry {
Name = "data.txt",
Type = "script",
Offset = 0,
Size = (uint)file.MaxOffset / 4 * 3,
}
};
return new ArcFile (file, this, dir);
}
public override Stream OpenEntry (ArcFile arc, Entry entry)
{
using (var input = arc.File.CreateStream())
using (var reader = new StreamReader (input))
{
var sourceString = reader.ReadToEnd();
var text = DecryptString (sourceString, DefaultPassword);
return new BinMemoryStream (text, entry.Name);
}
}
static byte[] DecryptString (string sourceString, string password)
{
var rijndaelManaged = new RijndaelManaged();
byte[] key, iv;
GenerateKeyFromPassword (password, rijndaelManaged.KeySize, out key, rijndaelManaged.BlockSize, out iv);
rijndaelManaged.Key = key;
rijndaelManaged.IV = iv;
var array = Convert.FromBase64String (sourceString);
using (var cryptoTransform = rijndaelManaged.CreateDecryptor())
{
return cryptoTransform.TransformFinalBlock (array, 0, array.Length);
}
}
static readonly byte[] DefaultSalt = Encoding.UTF8.GetBytes("saltは必ず8バイト以上");
static void GenerateKeyFromPassword (string password, int keySize, out byte[] key, int blockSize, out byte[] iv)
{
var rfc2898DeriveBytes = new Rfc2898DeriveBytes (password, DefaultSalt);
rfc2898DeriveBytes.IterationCount = 1000;
key = rfc2898DeriveBytes.GetBytes (keySize / 8);
iv = rfc2898DeriveBytes.GetBytes (blockSize / 8);
}
}
}

View File

@ -87,6 +87,7 @@ namespace GameRes.Formats.Unity
case 1: case 1:
index_data = UnpackLzma (packed, index_size); index_data = UnpackLzma (packed, index_size);
break; break;
case 2:
case 3: case 3:
index_data = new byte[index_size]; index_data = new byte[index_size];
Lz4Compressor.DecompressBlock (packed, packed.Length, index_data, index_data.Length); Lz4Compressor.DecompressBlock (packed, packed.Length, index_data, index_data.Length);

View File

@ -41,7 +41,7 @@ namespace GameRes.Formats.Unity
internal class Asset internal class Asset
{ {
int m_format; int m_format;
uint m_data_offset; long m_data_offset;
bool m_is_little_endian; bool m_is_little_endian;
UnityTypeData m_tree = new UnityTypeData(); UnityTypeData m_tree = new UnityTypeData();
Dictionary<long, int> m_adds; Dictionary<long, int> m_adds;
@ -63,6 +63,13 @@ namespace GameRes.Formats.Unity
m_data_offset = input.ReadUInt32(); m_data_offset = input.ReadUInt32();
if (m_format >= 9) if (m_format >= 9)
m_is_little_endian = 0 == input.ReadInt32(); m_is_little_endian = 0 == input.ReadInt32();
if (m_format >= 22)
{
input.ReadInt32(); // header_size
input.ReadInt64(); // file_size
m_data_offset = input.ReadInt64();
input.ReadInt64();
}
input.SetupReaders (this); input.SetupReaders (this);
m_tree.Load (input); m_tree.Load (input);
@ -176,7 +183,8 @@ namespace GameRes.Formats.Unity
public void Load (AssetReader reader) public void Load (AssetReader reader)
{ {
PathId = reader.ReadId(); PathId = reader.ReadId();
Offset = reader.ReadUInt32() + Asset.DataOffset; Offset = reader.ReadOffset();
Offset += Asset.DataOffset;
Size = reader.ReadUInt32(); Size = reader.ReadUInt32();
if (Asset.Format < 17) if (Asset.Format < 17)
{ {

View File

@ -65,6 +65,7 @@ namespace GameRes.Formats.Unity
public Func<int> ReadInt32; public Func<int> ReadInt32;
public Func<long> ReadInt64; public Func<long> ReadInt64;
public Func<long> ReadId; public Func<long> ReadId;
public Func<long> ReadOffset;
public void SetupReaders (Asset asset) public void SetupReaders (Asset asset)
{ {
@ -109,6 +110,10 @@ namespace GameRes.Formats.Unity
ReadId = ReadInt64; ReadId = ReadInt64;
else else
ReadId = () => ReadInt32(); ReadId = () => ReadInt32();
if (m_format >= 22)
ReadOffset = ReadInt64;
else
ReadOffset = () => ReadUInt32();
} }
/// <summary> /// <summary>
@ -122,6 +127,11 @@ namespace GameRes.Formats.Unity
ReadId = () => ReadInt32(); ReadId = () => ReadInt32();
} }
public void Skip (int count)
{
m_input.Seek (count, SeekOrigin.Current);
}
/// <summary> /// <summary>
/// Read bytes into specified buffer. /// Read bytes into specified buffer.
/// </summary> /// </summary>

View File

@ -97,13 +97,13 @@ namespace GameRes.Formats.Unity
internal class StreamingInfo internal class StreamingInfo
{ {
public uint Offset; public long Offset;
public uint Size; public uint Size;
public string Path; public string Path;
public void Load (AssetReader reader) public void Load (AssetReader reader)
{ {
Offset = reader.ReadUInt32(); Offset = reader.ReadOffset();
Size = reader.ReadUInt32(); Size = reader.ReadUInt32();
Path = reader.ReadString(); Path = reader.ReadString();
} }

View File

@ -0,0 +1,571 @@
//! \file Bc7Decoder.cs
//! \date 2022 May 03
//! \brief BC7 texture compression decoder.
//
// Based on the [bc7enc](https://github.com/richgel999/bc7enc)
//
// Copyright(c) 2020 Richard Geldreich, Jr.
//
// C# port copyright (C) 2022 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 GameRes.Utility;
namespace GameRes.Formats.Unity
{
public class Bc7Decoder
{
byte[] m_input;
int m_width;
int m_height;
int m_output_stride;
byte[] m_output;
byte[] m_block = new byte[64];
public Bc7Decoder (byte[] input, ImageMetaData info)
{
m_input = input;
m_width = info.iWidth;
m_output_stride = m_width * 4;
m_height = info.iHeight;
m_output = new byte[m_output_stride*m_height];
}
public byte[] Unpack ()
{
int block_step_y = m_output_stride * 4;
int block_step_x = 16;
int src = 0;
for (int y = 0; y < m_output.Length; y += block_step_y)
for (int x = 0; x < m_output_stride; x += block_step_x)
{
DecompressBc7Block (src);
int dst = y + x;
int block_src = 0;
for (int i = 0; i < 4; ++i)
{
int row_dst = dst;
for (int j = 0; j < 4; ++j)
{
m_output[row_dst++] = m_block[block_src+2];
m_output[row_dst++] = m_block[block_src+1];
m_output[row_dst++] = m_block[block_src ];
m_output[row_dst++] = m_block[block_src+3];
block_src += 4;
}
dst += m_output_stride;
}
src += 16;
}
return m_output;
}
private bool DecompressBc7Block (int src)
{
byte first_byte = m_input[src];
for (int mode = 0; mode < 8; ++mode)
{
if ((first_byte & (1u << mode)) != 0)
{
switch (mode)
{
case 0:
case 2:
return UnpackBc7Mode0_2 (mode, src);
case 1:
case 3:
case 7:
return UnpackBc7Mode1_3_7 (mode, src);
case 4:
case 5:
return UnpackBc7Mode4_5 (mode, src);
case 6:
return UnpackBc7Mode6 (src);
}
}
}
return false;
}
int m_bit_offset;
uint ReadBits32 (int src, int codesize)
{
uint bits = 0;
int total_bits = 0;
while (total_bits < codesize)
{
int byte_bit_offset = m_bit_offset & 7;
int bits_to_read = Math.Min (codesize - total_bits, 8 - byte_bit_offset);
uint byte_bits = (uint)m_input[src + (m_bit_offset >> 3)] >> byte_bit_offset;
byte_bits &= ((1u << bits_to_read) - 1u);
bits |= (byte_bits << total_bits);
total_bits += bits_to_read;
m_bit_offset += bits_to_read;
}
return bits;
}
byte[] endpoints = new byte[8 * 4];
uint[] pbits = new uint[6];
uint[] weights = new uint[16];
uint[] a_weights = new uint[16];
int[] weight_bits = new int[2];
byte[,] block_colors= new byte[3,32];
bool UnpackBc7Mode0_2 (int mode, int src)
{
const uint ENDPOINTS = 6;
const uint COMPS = 3;
int WEIGHT_BITS = (mode == 0) ? 3 : 2;
int ENDPOINT_BITS = (mode == 0) ? 4 : 5;
int PBITS = (mode == 0) ? 6 : 0;
uint WEIGHT_VALS = 1u << WEIGHT_BITS;
m_bit_offset = 0;
if (ReadBits32 (src, mode + 1) != (1u << mode))
return false;
uint part = ReadBits32 (src, (mode == 0) ? 4 : 6);
for (uint c = 0; c < COMPS; c++)
for (uint e = 0; e < ENDPOINTS * 4; e += 4)
endpoints[e+c] = (byte)ReadBits32(src, ENDPOINT_BITS);
for (uint p = 0; p < PBITS; p++)
pbits[p] = ReadBits32 (src, 1);
for (uint i = 0; i < 16; i++)
weights[i] = ReadBits32 (src, ((i == 0) || (i == s_bc7_table_anchor_index_third_subset_1[part]) || (i == s_bc7_table_anchor_index_third_subset_2[part])) ? (WEIGHT_BITS - 1) : WEIGHT_BITS);
for (uint e = 0; e < ENDPOINTS * 4; e += 4)
for (uint c = 0; c < 4; c++)
endpoints[e+c] = (byte)((c == 3) ? 0xFF : (PBITS != 0 ? bc7_dequant(endpoints[e+c], pbits[e/4], ENDPOINT_BITS) : bc7_dequant(endpoints[e+c], ENDPOINT_BITS)));
for (uint s = 0; s < 3; s++)
for (uint i = 0; i < WEIGHT_VALS*4; i += 4)
{
for (uint c = 0; c < 3; c++)
block_colors[s,i+c] = (byte)bc7_interp(endpoints[s * 8 + c], endpoints[s * 8 + 4 + c], i/4, WEIGHT_BITS);
block_colors[s,i+3] = 0xFF;
}
for (uint i = 0; i < 16*4; i += 4)
{
int b = s_bc7_partition3[part * 16 + i/4];
uint c = weights[i/4] * 4;
m_block[i ] = block_colors[b,c];
m_block[i+1] = block_colors[b,c+1];
m_block[i+2] = block_colors[b,c+2];
m_block[i+3] = block_colors[b,c+3];
}
return true;
}
bool UnpackBc7Mode1_3_7 (int mode, int src)
{
const uint ENDPOINTS = 4;
int COMPS = (mode == 7) ? 4 : 3;
int WEIGHT_BITS = (mode == 1) ? 3 : 2;
int ENDPOINT_BITS = (mode == 7) ? 5 : ((mode == 1) ? 6 : 7);
int PBITS = (mode == 1) ? 2 : 4;
bool SHARED_PBITS = mode == 1;
uint WEIGHT_VALS = 1u << WEIGHT_BITS;
m_bit_offset = 0;
if (ReadBits32 (src, mode + 1) != (1u << mode))
return false;
uint part = ReadBits32 (src, 6);
for (uint c = 0; c < COMPS; c++)
for (uint e = 0; e < ENDPOINTS * 4; e += 4)
endpoints[e+c] = (byte)ReadBits32(src, ENDPOINT_BITS);
for (uint p = 0; p < PBITS; p++)
pbits[p] = ReadBits32 (src, 1);
for (uint i = 0; i < 16; i++)
weights[i] = ReadBits32(src, ((i == 0) || (i == s_bc7_table_anchor_index_second_subset[part])) ? (WEIGHT_BITS - 1) : WEIGHT_BITS);
for (uint e = 0; e < ENDPOINTS*4; e += 4)
for (uint c = 0; c < 4; c++)
endpoints[e+c] = (byte)((c == ((mode == 7u) ? 4u : 3u)) ? 0xFF : bc7_dequant(endpoints[e+c], pbits[SHARED_PBITS ? ((e/4) >> 1) : (e/4)], ENDPOINT_BITS));
for (uint s = 0; s < 2; s++)
for (uint i = 0; i < WEIGHT_VALS*4; i += 4)
{
for (uint c = 0; c < COMPS; c++)
block_colors[s,i+c] = (byte)bc7_interp(endpoints[(s * 2)*4+c], endpoints[(s * 2 + 1)*4+c], i/4, WEIGHT_BITS);
block_colors[s,i+3] = (COMPS == 3) ? (byte)0xFF : block_colors[s,i+3];
}
for (uint i = 0; i < 16*4; i += 4)
{
int b = s_bc7_partition2[part * 16 + i/4];
uint c = weights[i/4] * 4;
m_block[i ] = block_colors[b,c];
m_block[i+1] = block_colors[b,c+1];
m_block[i+2] = block_colors[b,c+2];
m_block[i+3] = block_colors[b,c+3];
}
return true;
}
bool UnpackBc7Mode4_5 (int mode, int src)
{
const uint ENDPOINTS = 2;
const uint COMPS = 4;
const int WEIGHT_BITS = 2;
int A_WEIGHT_BITS = (mode == 4) ? 3 : 2;
int ENDPOINT_BITS = (mode == 4) ? 5 : 7;
int A_ENDPOINT_BITS = (mode == 4) ? 6 : 8;
m_bit_offset = 0;
if (ReadBits32(src, mode + 1) != (1u << mode))
return false;
uint comp_rot = ReadBits32 (src, 2);
uint index_mode = (mode == 4) ? ReadBits32 (src, 1) : 0;
for (uint c = 0; c < COMPS; c++)
for (uint e = 0; e < ENDPOINTS*4; e += 4)
endpoints[e+c] = (byte)ReadBits32(src, (c == 3) ? A_ENDPOINT_BITS : ENDPOINT_BITS);
weight_bits[0] = index_mode != 0 ? A_WEIGHT_BITS : WEIGHT_BITS;
weight_bits[1] = index_mode != 0 ? WEIGHT_BITS : A_WEIGHT_BITS;
uint[] w_array = index_mode != 0 ? a_weights : weights;
for (uint i = 0; i < 16; i++)
w_array[i] = ReadBits32 (src, weight_bits[index_mode] - ((i == 0) ? 1 : 0));
w_array = index_mode != 0 ? weights : a_weights;
for (uint i = 0; i < 16; i++)
w_array[i] = ReadBits32 (src, weight_bits[1 - index_mode] - ((i == 0) ? 1 : 0));
for (uint e = 0; e < ENDPOINTS*4; e += 4)
for (uint c = 0; c < 4; c++)
endpoints[e+c] = (byte)bc7_dequant(endpoints[e+c], (c == 3) ? A_ENDPOINT_BITS : ENDPOINT_BITS);
for (uint i = 0; i < (1U << weight_bits[0]) * 4; i += 4)
for (uint c = 0; c < 3; c++)
block_colors[0,i+c] = (byte)bc7_interp(endpoints[c], endpoints[4+c], i/4, weight_bits[0]);
for (uint i = 0; i < (1U << weight_bits[1]) * 4; i += 4)
block_colors[0,i+3] = (byte)bc7_interp(endpoints[3], endpoints[4+3], i/4, weight_bits[1]);
for (uint i = 0; i < 16*4; i += 4)
{
uint w = weights[i / 4] * 4;
m_block[i ] = block_colors[0,w];
m_block[i+1] = block_colors[0,w+1];
m_block[i+2] = block_colors[0,w+2];
m_block[i+3] = block_colors[0,a_weights[i/4]*4+3];
if (comp_rot >= 1)
{
byte a = m_block[i+3];
m_block[i+3] = m_block[i+comp_rot-1];
m_block[i+comp_rot-1] = a;
}
}
return true;
}
internal class Bc7Mode_6
{
public struct Lo
{
public byte mode ; //: 7;
public byte r0 ; //: 7;
public byte r1 ; //: 7;
public byte g0 ; //: 7;
public byte g1 ; //: 7;
public byte b0 ; //: 7;
public byte b1 ; //: 7;
public byte a0 ; //: 7;
public byte a1 ; //: 7;
public byte p0 ; //: 1;
}
public struct Hi
{
public byte p1 ; //: 1;
public byte s00 ; //: 3;
public byte s10 ; //: 4;
public byte s20 ; //: 4;
public byte s30 ; //: 4;
public byte s01 ; //: 4;
public byte s11 ; //: 4;
public byte s21 ; //: 4;
public byte s31 ; //: 4;
public byte s02 ; //: 4;
public byte s12 ; //: 4;
public byte s22 ; //: 4;
public byte s32 ; //: 4;
public byte s03 ; //: 4;
public byte s13 ; //: 4;
public byte s23 ; //: 4;
public byte s33 ; //: 4;
}
public Lo m_lo;
public Hi m_hi;
public void Unpack (byte[] input, int src)
{
ulong lo_bits = input.ToUInt64 (src);
ulong hi_bits = input.ToUInt64 (src+8);
m_lo.mode = (byte)( lo_bits & 0x7F);
m_lo.r0 = (byte)((lo_bits >> 7) & 0x7F);
m_lo.r1 = (byte)((lo_bits >> 14) & 0x7F);
m_lo.g0 = (byte)((lo_bits >> 21) & 0x7F);
m_lo.g1 = (byte)((lo_bits >> 28) & 0x7F);
m_lo.b0 = (byte)((lo_bits >> 35) & 0x7F);
m_lo.b1 = (byte)((lo_bits >> 42) & 0x7F);
m_lo.a0 = (byte)((lo_bits >> 49) & 0x7F);
m_lo.a1 = (byte)((lo_bits >> 56) & 0x7F);
m_lo.p0 = (byte)((lo_bits >> 63));
m_hi.p1 = (byte)((hi_bits & 1));
m_hi.s00 = (byte)((hi_bits >> 1) & 0x7);
m_hi.s10 = (byte)((hi_bits >> 4) & 0xF);
m_hi.s20 = (byte)((hi_bits >> 8) & 0xF);
m_hi.s30 = (byte)((hi_bits >> 12) & 0xF);
m_hi.s01 = (byte)((hi_bits >> 16) & 0xF);
m_hi.s11 = (byte)((hi_bits >> 20) & 0xF);
m_hi.s21 = (byte)((hi_bits >> 24) & 0xF);
m_hi.s31 = (byte)((hi_bits >> 28) & 0xF);
m_hi.s02 = (byte)((hi_bits >> 32) & 0xF);
m_hi.s12 = (byte)((hi_bits >> 36) & 0xF);
m_hi.s22 = (byte)((hi_bits >> 40) & 0xF);
m_hi.s32 = (byte)((hi_bits >> 44) & 0xF);
m_hi.s03 = (byte)((hi_bits >> 48) & 0xF);
m_hi.s13 = (byte)((hi_bits >> 52) & 0xF);
m_hi.s23 = (byte)((hi_bits >> 56) & 0xF);
m_hi.s33 = (byte)((hi_bits >> 60));
}
}
Bc7Mode_6 mode_6_block = new Bc7Mode_6();
uint[] vals = new uint[16];
bool UnpackBc7Mode6(int src)
{
mode_6_block.Unpack (m_input, src);
var block = mode_6_block;
if (block.m_lo.mode != (1 << 6))
return false;
uint r0 = (uint)((block.m_lo.r0 << 1) | block.m_lo.p0);
uint g0 = (uint)((block.m_lo.g0 << 1) | block.m_lo.p0);
uint b0 = (uint)((block.m_lo.b0 << 1) | block.m_lo.p0);
uint a0 = (uint)((block.m_lo.a0 << 1) | block.m_lo.p0);
uint r1 = (uint)((block.m_lo.r1 << 1) | block.m_hi.p1);
uint g1 = (uint)((block.m_lo.g1 << 1) | block.m_hi.p1);
uint b1 = (uint)((block.m_lo.b1 << 1) | block.m_hi.p1);
uint a1 = (uint)((block.m_lo.a1 << 1) | block.m_hi.p1);
for (int i = 0; i < 16; i++)
{
uint w = s_bc7_weights4[i];
uint iw = 64 - w;
SetNoclampRgba(vals, i,
(r0 * iw + r1 * w + 32u) >> 6,
(g0 * iw + g1 * w + 32u) >> 6,
(b0 * iw + b1 * w + 32u) >> 6,
(a0 * iw + a1 * w + 32u) >> 6);
}
LittleEndian.Pack (vals[block.m_hi.s00], m_block, 0);
LittleEndian.Pack (vals[block.m_hi.s10], m_block, 4);
LittleEndian.Pack (vals[block.m_hi.s20], m_block, 8);
LittleEndian.Pack (vals[block.m_hi.s30], m_block, 12);
LittleEndian.Pack (vals[block.m_hi.s01], m_block, 16);
LittleEndian.Pack (vals[block.m_hi.s11], m_block, 20);
LittleEndian.Pack (vals[block.m_hi.s21], m_block, 24);
LittleEndian.Pack (vals[block.m_hi.s31], m_block, 28);
LittleEndian.Pack (vals[block.m_hi.s02], m_block, 32);
LittleEndian.Pack (vals[block.m_hi.s12], m_block, 36);
LittleEndian.Pack (vals[block.m_hi.s22], m_block, 40);
LittleEndian.Pack (vals[block.m_hi.s32], m_block, 44);
LittleEndian.Pack (vals[block.m_hi.s03], m_block, 48);
LittleEndian.Pack (vals[block.m_hi.s13], m_block, 52);
LittleEndian.Pack (vals[block.m_hi.s23], m_block, 56);
LittleEndian.Pack (vals[block.m_hi.s33], m_block, 60);
return true;
}
static void SetNoclampRgba (uint[] vals, int dst, uint sr, uint sg, uint sb, uint sa)
{
vals[dst] = (sr & 0xFF) | ((sg & 0xFF) << 8) | ((sb & 0xFF) << 16) | ((sa & 0xFF) << 24);
}
static uint bc7_dequant (uint val, uint pbit, int val_bits)
{
int total_bits = val_bits + 1;
val = (val << 1) | pbit;
val <<= (8 - total_bits);
val |= (val >> total_bits);
return val;
}
static uint bc7_dequant (uint val, int val_bits)
{
val <<= (8 - val_bits);
val |= (val >> val_bits);
return val;
}
static uint bc7_interp2 (uint l, uint h, uint w)
{
return (l * (64 - s_bc7_weights2[w]) + h * s_bc7_weights2[w] + 32) >> 6;
}
static uint bc7_interp3 (uint l, uint h, uint w)
{
return (l * (64 - s_bc7_weights3[w]) + h * s_bc7_weights3[w] + 32) >> 6;
}
static uint bc7_interp4 (uint l, uint h, uint w)
{
return (l * (64 - s_bc7_weights4[w]) + h * s_bc7_weights4[w] + 32) >> 6;
}
static uint bc7_interp (uint l, uint h, uint w, int bits)
{
switch (bits)
{
case 2: return bc7_interp2 (l, h, w);
case 3: return bc7_interp3 (l, h, w);
case 4: return bc7_interp4 (l, h, w);
default: return 0;
}
}
static readonly uint[] s_bc7_weights2 = { 0, 21, 43, 64 };
static readonly uint[] s_bc7_weights3 = { 0, 9, 18, 27, 37, 46, 55, 64 };
static readonly uint[] s_bc7_weights4 = { 0, 4, 9, 13, 17, 21, 26, 30, 34, 38, 43, 47, 51, 55, 60, 64 };
static readonly byte[] s_bc7_partition2 = {
0,0,1,1,0,0,1,1,0,0,1,1,0,0,1,1, 0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1,
0,1,1,1,0,1,1,1,0,1,1,1,0,1,1,1, 0,0,0,1,0,0,1,1,0,0,1,1,0,1,1,1,
0,0,0,0,0,0,0,1,0,0,0,1,0,0,1,1, 0,0,1,1,0,1,1,1,0,1,1,1,1,1,1,1,
0,0,0,1,0,0,1,1,0,1,1,1,1,1,1,1, 0,0,0,0,0,0,0,1,0,0,1,1,0,1,1,1,
0,0,0,0,0,0,0,0,0,0,0,1,0,0,1,1, 0,0,1,1,0,1,1,1,1,1,1,1,1,1,1,1,
0,0,0,0,0,0,0,1,0,1,1,1,1,1,1,1, 0,0,0,0,0,0,0,0,0,0,0,1,0,1,1,1,
0,0,0,1,0,1,1,1,1,1,1,1,1,1,1,1, 0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,
0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1, 0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,
0,0,0,0,1,0,0,0,1,1,1,0,1,1,1,1, 0,1,1,1,0,0,0,1,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,1,0,0,0,1,1,1,0, 0,1,1,1,0,0,1,1,0,0,0,1,0,0,0,0,
0,0,1,1,0,0,0,1,0,0,0,0,0,0,0,0, 0,0,0,0,1,0,0,0,1,1,0,0,1,1,1,0,
0,0,0,0,0,0,0,0,1,0,0,0,1,1,0,0, 0,1,1,1,0,0,1,1,0,0,1,1,0,0,0,1,
0,0,1,1,0,0,0,1,0,0,0,1,0,0,0,0, 0,0,0,0,1,0,0,0,1,0,0,0,1,1,0,0,
0,1,1,0,0,1,1,0,0,1,1,0,0,1,1,0, 0,0,1,1,0,1,1,0,0,1,1,0,1,1,0,0,
0,0,0,1,0,1,1,1,1,1,1,0,1,0,0,0, 0,0,0,0,1,1,1,1,1,1,1,1,0,0,0,0,
0,1,1,1,0,0,0,1,1,0,0,0,1,1,1,0, 0,0,1,1,1,0,0,1,1,0,0,1,1,1,0,0,
0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1, 0,0,0,0,1,1,1,1,0,0,0,0,1,1,1,1,
0,1,0,1,1,0,1,0,0,1,0,1,1,0,1,0, 0,0,1,1,0,0,1,1,1,1,0,0,1,1,0,0,
0,0,1,1,1,1,0,0,0,0,1,1,1,1,0,0, 0,1,0,1,0,1,0,1,1,0,1,0,1,0,1,0,
0,1,1,0,1,0,0,1,0,1,1,0,1,0,0,1, 0,1,0,1,1,0,1,0,1,0,1,0,0,1,0,1,
0,1,1,1,0,0,1,1,1,1,0,0,1,1,1,0, 0,0,0,1,0,0,1,1,1,1,0,0,1,0,0,0,
0,0,1,1,0,0,1,0,0,1,0,0,1,1,0,0, 0,0,1,1,1,0,1,1,1,1,0,1,1,1,0,0,
0,1,1,0,1,0,0,1,1,0,0,1,0,1,1,0, 0,0,1,1,1,1,0,0,1,1,0,0,0,0,1,1,
0,1,1,0,0,1,1,0,1,0,0,1,1,0,0,1, 0,0,0,0,0,1,1,0,0,1,1,0,0,0,0,0,
0,1,0,0,1,1,1,0,0,1,0,0,0,0,0,0, 0,0,1,0,0,1,1,1,0,0,1,0,0,0,0,0,
0,0,0,0,0,0,1,0,0,1,1,1,0,0,1,0, 0,0,0,0,0,1,0,0,1,1,1,0,0,1,0,0,
0,1,1,0,1,1,0,0,1,0,0,1,0,0,1,1, 0,0,1,1,0,1,1,0,1,1,0,0,1,0,0,1,
0,1,1,0,0,0,1,1,1,0,0,1,1,1,0,0, 0,0,1,1,1,0,0,1,1,1,0,0,0,1,1,0,
0,1,1,0,1,1,0,0,1,1,0,0,1,0,0,1, 0,1,1,0,0,0,1,1,0,0,1,1,1,0,0,1,
0,1,1,1,1,1,1,0,1,0,0,0,0,0,0,1, 0,0,0,1,1,0,0,0,1,1,1,0,0,1,1,1,
0,0,0,0,1,1,1,1,0,0,1,1,0,0,1,1, 0,0,1,1,0,0,1,1,1,1,1,1,0,0,0,0,
0,0,1,0,0,0,1,0,1,1,1,0,1,1,1,0, 0,1,0,0,0,1,0,0,0,1,1,1,0,1,1,1
};
static readonly byte[] s_bc7_partition3 = {
0,0,1,1,0,0,1,1,0,2,2,1,2,2,2,2, 0,0,0,1,0,0,1,1,2,2,1,1,2,2,2,1,
0,0,0,0,2,0,0,1,2,2,1,1,2,2,1,1, 0,2,2,2,0,0,2,2,0,0,1,1,0,1,1,1,
0,0,0,0,0,0,0,0,1,1,2,2,1,1,2,2, 0,0,1,1,0,0,1,1,0,0,2,2,0,0,2,2,
0,0,2,2,0,0,2,2,1,1,1,1,1,1,1,1, 0,0,1,1,0,0,1,1,2,2,1,1,2,2,1,1,
0,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2, 0,0,0,0,1,1,1,1,1,1,1,1,2,2,2,2,
0,0,0,0,1,1,1,1,2,2,2,2,2,2,2,2, 0,0,1,2,0,0,1,2,0,0,1,2,0,0,1,2,
0,1,1,2,0,1,1,2,0,1,1,2,0,1,1,2, 0,1,2,2,0,1,2,2,0,1,2,2,0,1,2,2,
0,0,1,1,0,1,1,2,1,1,2,2,1,2,2,2, 0,0,1,1,2,0,0,1,2,2,0,0,2,2,2,0,
0,0,0,1,0,0,1,1,0,1,1,2,1,1,2,2, 0,1,1,1,0,0,1,1,2,0,0,1,2,2,0,0,
0,0,0,0,1,1,2,2,1,1,2,2,1,1,2,2, 0,0,2,2,0,0,2,2,0,0,2,2,1,1,1,1,
0,1,1,1,0,1,1,1,0,2,2,2,0,2,2,2, 0,0,0,1,0,0,0,1,2,2,2,1,2,2,2,1,
0,0,0,0,0,0,1,1,0,1,2,2,0,1,2,2, 0,0,0,0,1,1,0,0,2,2,1,0,2,2,1,0,
0,1,2,2,0,1,2,2,0,0,1,1,0,0,0,0, 0,0,1,2,0,0,1,2,1,1,2,2,2,2,2,2,
0,1,1,0,1,2,2,1,1,2,2,1,0,1,1,0, 0,0,0,0,0,1,1,0,1,2,2,1,1,2,2,1,
0,0,2,2,1,1,0,2,1,1,0,2,0,0,2,2, 0,1,1,0,0,1,1,0,2,0,0,2,2,2,2,2,
0,0,1,1,0,1,2,2,0,1,2,2,0,0,1,1, 0,0,0,0,2,0,0,0,2,2,1,1,2,2,2,1,
0,0,0,0,0,0,0,2,1,1,2,2,1,2,2,2, 0,2,2,2,0,0,2,2,0,0,1,2,0,0,1,1,
0,0,1,1,0,0,1,2,0,0,2,2,0,2,2,2, 0,1,2,0,0,1,2,0,0,1,2,0,0,1,2,0,
0,0,0,0,1,1,1,1,2,2,2,2,0,0,0,0, 0,1,2,0,1,2,0,1,2,0,1,2,0,1,2,0,
0,1,2,0,2,0,1,2,1,2,0,1,0,1,2,0, 0,0,1,1,2,2,0,0,1,1,2,2,0,0,1,1,
0,0,1,1,1,1,2,2,2,2,0,0,0,0,1,1, 0,1,0,1,0,1,0,1,2,2,2,2,2,2,2,2,
0,0,0,0,0,0,0,0,2,1,2,1,2,1,2,1, 0,0,2,2,1,1,2,2,0,0,2,2,1,1,2,2,
0,0,2,2,0,0,1,1,0,0,2,2,0,0,1,1, 0,2,2,0,1,2,2,1,0,2,2,0,1,2,2,1,
0,1,0,1,2,2,2,2,2,2,2,2,0,1,0,1, 0,0,0,0,2,1,2,1,2,1,2,1,2,1,2,1,
0,1,0,1,0,1,0,1,0,1,0,1,2,2,2,2, 0,2,2,2,0,1,1,1,0,2,2,2,0,1,1,1,
0,0,0,2,1,1,1,2,0,0,0,2,1,1,1,2, 0,0,0,0,2,1,1,2,2,1,1,2,2,1,1,2,
0,2,2,2,0,1,1,1,0,1,1,1,0,2,2,2, 0,0,0,2,1,1,1,2,1,1,1,2,0,0,0,2,
0,1,1,0,0,1,1,0,0,1,1,0,2,2,2,2, 0,0,0,0,0,0,0,0,2,1,1,2,2,1,1,2,
0,1,1,0,0,1,1,0,2,2,2,2,2,2,2,2, 0,0,2,2,0,0,1,1,0,0,1,1,0,0,2,2,
0,0,2,2,1,1,2,2,1,1,2,2,0,0,2,2, 0,0,0,0,0,0,0,0,0,0,0,0,2,1,1,2,
0,0,0,2,0,0,0,1,0,0,0,2,0,0,0,1, 0,2,2,2,1,2,2,2,0,2,2,2,1,2,2,2,
0,1,0,1,2,2,2,2,2,2,2,2,2,2,2,2, 0,1,1,1,2,0,1,1,2,2,0,1,2,2,2,0,
};
static readonly byte[] s_bc7_table_anchor_index_second_subset = {
15,15,15,15,15,15,15,15, 15,15,15,15,15,15,15,15,
15, 2, 8, 2, 2, 8, 8,15, 2, 8, 2, 2, 8, 8, 2, 2,
15,15, 6, 8, 2, 8,15,15, 2, 8, 2, 2, 2,15,15, 6,
6, 2, 6, 8,15,15, 2, 2, 15,15,15,15,15, 2, 2,15
};
static readonly byte[] s_bc7_table_anchor_index_third_subset_1 = {
3, 3,15,15, 8, 3,15,15, 8, 8, 6, 6, 6, 5, 3, 3,
3, 3, 8,15, 3, 3, 6,10, 5, 8, 8, 6, 8, 5,15,15,
8,15, 3, 5, 6,10, 8,15, 15, 3,15, 5,15,15,15,15,
3,15, 5, 5, 5, 8, 5,10, 5,10, 8,13,15,12, 3, 3
};
static readonly byte[] s_bc7_table_anchor_index_third_subset_2 = {
15, 8, 8, 3,15,15, 3, 8, 15,15,15,15,15,15,15, 8,
15, 8,15, 3,15, 8,15, 8, 3,15, 6,10,15,15,10, 8,
15, 3,15,10,10, 8, 9,10, 6,15, 8,15, 3, 6, 6, 8,
15, 3,15,15,15,15,15,15, 15,15,15,15, 3,15,15, 8
};
}
}

View File

@ -118,7 +118,7 @@ namespace GameRes.Formats.Unity
m_packed = new byte[segment.PackedSize]; m_packed = new byte[segment.PackedSize];
int packed_size = m_input.Read (m_packed, 0, (int)segment.PackedSize); int packed_size = m_input.Read (m_packed, 0, (int)segment.PackedSize);
var output = PrepareBuffer (segment.UnpackedSize); var output = PrepareBuffer (segment.UnpackedSize);
if (3 == method) if (3 == method || 2 == method)
m_buffer_len = Lz4Compressor.DecompressBlock (m_packed, packed_size, output, (int)segment.UnpackedSize); m_buffer_len = Lz4Compressor.DecompressBlock (m_packed, packed_size, output, (int)segment.UnpackedSize);
else else
throw new NotImplementedException ("Not supported Unity asset bundle compression."); throw new NotImplementedException ("Not supported Unity asset bundle compression.");

View File

@ -208,7 +208,7 @@ namespace GameRes.Formats.Vorbis
GranuleVals[LacingFill + i] = GranulePos; GranuleVals[LacingFill + i] = GranulePos;
} }
LacingVals[LacingFill + i] = bytes % 0xFF; LacingVals[LacingFill + i] = bytes % 0xFF;
GranulePos = GranuleVals[LacingFill+i] = GranulePos; GranulePos = GranuleVals[LacingFill+i] = op.GranulePos;
// flag the first segment as the beginning of the packet // flag the first segment as the beginning of the packet
LacingVals[LacingFill] |= 0x100; LacingVals[LacingFill] |= 0x100;

View File

@ -61,7 +61,7 @@ namespace GameRes.Formats.Unity
case 28: // Texture2D case 28: // Texture2D
{ {
var tex = new Texture2D(); var tex = new Texture2D();
tex.Load (input, asset.Tree.Version); tex.Load (input, asset.Tree);
if (0 == tex.m_DataLength) if (0 == tex.m_DataLength)
{ {
var stream_data = new StreamingInfo(); var stream_data = new StreamingInfo();

View File

@ -97,12 +97,17 @@ namespace GameRes.Formats.Unity
m_DataLength = reader.ReadInt32(); m_DataLength = reader.ReadInt32();
} }
public void Load (AssetReader reader, string version) public void Load (AssetReader reader, UnityTypeData type)
{ {
if (version != "2017.3.1f1") if ("2021.1.3f1" == type.Version) // type.Hashes[28] == [0D 08 41 4C FD 5B DB 0D 22 79 20 11 BD A9 AB 26]
{
Load2021 (reader);
return;
}
if (type.Version != "2017.3.1f1")
{ {
Load (reader); Load (reader);
if (0 == m_DataLength && version.StartsWith ("2017.")) // "2017.2.0f3" || "2017.1.1p1" if (0 == m_DataLength && type.Version.StartsWith ("2017.")) // "2017.2.0f3" || "2017.1.1p1"
reader.ReadInt64(); reader.ReadInt64();
return; return;
} }
@ -130,6 +135,35 @@ namespace GameRes.Formats.Unity
m_DataLength = reader.ReadInt32(); m_DataLength = reader.ReadInt32();
} }
public void Load2021 (AssetReader reader)
{
m_Name = reader.ReadString();
reader.Align();
reader.ReadInt32(); // m_ForcedFallbackFormat
reader.ReadInt32(); // m_DownscaleFallback
m_Width = reader.ReadInt32();
m_Height = reader.ReadInt32();
m_CompleteImageSize = reader.ReadInt32();
reader.ReadInt32(); // m_MipsStripped;
m_TextureFormat = (TextureFormat)reader.ReadInt32();
m_MipCount = reader.ReadInt32();
m_IsReadable = reader.ReadBool();
reader.Align();
reader.ReadInt32(); // m_StreamingMipmapsPriority
m_ImageCount = reader.ReadInt32();
m_TextureDimension = reader.ReadInt32();
m_FilterMode = reader.ReadInt32();
m_Aniso = reader.ReadInt32();
m_MipBias = reader.ReadFloat();
m_WrapMode = reader.ReadInt32(); // m_WrapU
reader.ReadInt32(); // m_WrapV
reader.ReadInt32(); // m_WrapW
reader.ReadInt32(); // m_LightmapFormat
m_ColorSpace = reader.ReadInt32();
reader.ReadInt32();
m_DataLength = reader.ReadInt32();
}
public void LoadData (AssetReader reader) public void LoadData (AssetReader reader)
{ {
m_Data = reader.ReadBytes (m_DataLength); m_Data = reader.ReadBytes (m_DataLength);
@ -243,6 +277,12 @@ namespace GameRes.Formats.Unity
pixels = ConvertArgb16 (m_texture.m_Data); pixels = ConvertArgb16 (m_texture.m_Data);
break; break;
case TextureFormat.BC7:
{
var decoder = new Bc7Decoder (m_texture.m_Data, Info);
pixels = decoder.Unpack();
break;
}
default: default:
throw new NotImplementedException (string.Format ("Not supported Unity Texture2D format '{0}'.", m_texture.m_TextureFormat)); throw new NotImplementedException (string.Format ("Not supported Unity Texture2D format '{0}'.", m_texture.m_TextureFormat));
} }

View File

@ -226,7 +226,7 @@
</dependentAssembly> </dependentAssembly>
<dependentAssembly> <dependentAssembly>
<assemblyIdentity name="System.Runtime.CompilerServices.Unsafe" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" /> <assemblyIdentity name="System.Runtime.CompilerServices.Unsafe" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-6.0.0.0" newVersion="6.0.0.0" /> <bindingRedirect oldVersion="0.0.0.0-4.0.5.0" newVersion="4.0.5.0" />
</dependentAssembly> </dependentAssembly>
</assemblyBinding> </assemblyBinding>
</runtime> </runtime>

View File

@ -1,14 +1,13 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<packages> <packages>
<package id="NVorbis" version="0.10.4" targetFramework="net461" /> <package id="NVorbis" version="0.10.4" targetFramework="net46" />
<package id="SharpZipLib" version="1.3.3" targetFramework="net461" /> <package id="SharpZipLib" version="1.3.3" targetFramework="net46" />
<package id="System.Buffers" version="4.5.1" targetFramework="net461" /> <package id="System.Buffers" version="4.5.1" targetFramework="net46" />
<package id="System.IO.FileSystem" version="4.3.0" targetFramework="net461" /> <package id="System.IO.FileSystem" version="4.3.0" targetFramework="net46" />
<package id="System.IO.FileSystem.Primitives" version="4.3.0" targetFramework="net461" /> <package id="System.IO.FileSystem.Primitives" version="4.3.0" targetFramework="net46" />
<package id="System.Memory" version="4.5.4" targetFramework="net461" /> <package id="System.Memory" version="4.5.4" targetFramework="net46" />
<package id="System.Numerics.Vectors" version="4.5.0" targetFramework="net461" /> <package id="System.Runtime.CompilerServices.Unsafe" version="4.6.0" targetFramework="net46" />
<package id="System.Runtime.CompilerServices.Unsafe" version="6.0.0" targetFramework="net461" /> <package id="System.Security.Cryptography.Algorithms" version="4.3.1" targetFramework="net46" />
<package id="System.Security.Cryptography.Algorithms" version="4.3.1" targetFramework="net461" /> <package id="System.Security.Cryptography.Primitives" version="4.3.0" targetFramework="net46" />
<package id="System.Security.Cryptography.Primitives" version="4.3.0" targetFramework="net461" /> <package id="System.ValueTuple" version="4.5.0" targetFramework="net46" />
<package id="System.ValueTuple" version="4.5.0" targetFramework="net461" />
</packages> </packages>

View File

@ -96,7 +96,7 @@ namespace GARbro
return; return;
} }
var tag = args[argn+1]; var tag = args[argn+1];
m_image_format = FindFormat (tag); m_image_format = ImageFormat.FindByTag (tag);
if (null == m_image_format) if (null == m_image_format)
{ {
Console.Error.WriteLine ("{0}: unknown format specified", tag); Console.Error.WriteLine ("{0}: unknown format specified", tag);
@ -132,7 +132,7 @@ namespace GARbro
{ {
VFS.ChDir (m_arc_name); VFS.ChDir (m_arc_name);
} }
catch (Exception X) catch (Exception)
{ {
Console.Error.WriteLine ("{0}: unknown format", m_arc_name); Console.Error.WriteLine ("{0}: unknown format", m_arc_name);
continue; continue;
@ -174,8 +174,9 @@ namespace GARbro
static void Usage () static void Usage ()
{ {
Console.WriteLine ("Usage: gameres [OPTIONS] ARC [ENTRIES]"); Console.WriteLine ("Usage: gameres [OPTIONS] ARC [ENTRIES]");
Console.WriteLine (" -l list recognized archive formats"); Console.WriteLine (" -l list recognized archive formats");
Console.WriteLine (" -x extract all files"); Console.WriteLine (" -x extract all files");
Console.WriteLine (" -c FORMAT convert images to specified format");
Console.WriteLine ("Without options displays contents of specified archive."); Console.WriteLine ("Without options displays contents of specified archive.");
} }

View File

@ -54,6 +54,7 @@
</Reference> </Reference>
<Reference Include="Concentus.Oggfile, Version=1.0.4.0, Culture=neutral, processorArchitecture=MSIL"> <Reference Include="Concentus.Oggfile, Version=1.0.4.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\packages\Concentus.Oggfile.1.0.4\lib\net45\Concentus.Oggfile.dll</HintPath> <HintPath>..\packages\Concentus.Oggfile.1.0.4\lib\net45\Concentus.Oggfile.dll</HintPath>
<Private>True</Private>
</Reference> </Reference>
<Reference Include="Microsoft.Deployment.Compression, Version=3.0.0.0, Culture=neutral, PublicKeyToken=ce35f76fcda82bad, processorArchitecture=MSIL"> <Reference Include="Microsoft.Deployment.Compression, Version=3.0.0.0, Culture=neutral, PublicKeyToken=ce35f76fcda82bad, processorArchitecture=MSIL">
<HintPath>..\packages\MSFTCompressionCab.1.0.0\lib\Microsoft.Deployment.Compression.dll</HintPath> <HintPath>..\packages\MSFTCompressionCab.1.0.0\lib\Microsoft.Deployment.Compression.dll</HintPath>
@ -68,6 +69,8 @@
<Reference Include="System" /> <Reference Include="System" />
<Reference Include="System.AppContext, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL"> <Reference Include="System.AppContext, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>..\packages\System.AppContext.4.3.0\lib\net46\System.AppContext.dll</HintPath> <HintPath>..\packages\System.AppContext.4.3.0\lib\net46\System.AppContext.dll</HintPath>
<Reference Include="System.Buffers, Version=4.0.3.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
<HintPath>..\packages\System.Buffers.4.5.1\lib\netstandard1.1\System.Buffers.dll</HintPath>
</Reference> </Reference>
<Reference Include="System.ComponentModel.Composition" /> <Reference Include="System.ComponentModel.Composition" />
<Reference Include="System.Console, Version=4.0.1.1, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL"> <Reference Include="System.Console, Version=4.0.1.1, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
@ -116,6 +119,23 @@
</Reference> </Reference>
<Reference Include="System.Security.Cryptography.X509Certificates, Version=4.1.1.2, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL"> <Reference Include="System.Security.Cryptography.X509Certificates, Version=4.1.1.2, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>..\packages\System.Security.Cryptography.X509Certificates.4.3.2\lib\net461\System.Security.Cryptography.X509Certificates.dll</HintPath> <HintPath>..\packages\System.Security.Cryptography.X509Certificates.4.3.2\lib\net461\System.Security.Cryptography.X509Certificates.dll</HintPath>
<Reference Include="System.IO.Compression, Version=4.1.2.0, Culture=neutral, PublicKeyToken=b77a5c561934e089, processorArchitecture=MSIL">
<HintPath>..\packages\System.IO.Compression.4.3.0\lib\net46\System.IO.Compression.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="System.Memory, Version=4.0.1.1, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
<HintPath>..\packages\System.Memory.4.5.4\lib\netstandard1.1\System.Memory.dll</HintPath>
</Reference>
<Reference Include="System.Net.Http, Version=4.1.1.3, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>..\packages\System.Net.Http.4.3.4\lib\net46\System.Net.Http.dll</HintPath>
</Reference>
<Reference Include="System.Numerics" />
<Reference Include="System.Runtime.CompilerServices.Unsafe, Version=4.0.5.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>..\packages\System.Runtime.CompilerServices.Unsafe.4.6.0\lib\netstandard1.0\System.Runtime.CompilerServices.Unsafe.dll</HintPath>
</Reference>
<Reference Include="System.Runtime.Serialization" />
<Reference Include="System.Security.Cryptography.X509Certificates, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>..\packages\System.Security.Cryptography.X509Certificates.4.3.2\lib\net46\System.Security.Cryptography.X509Certificates.dll</HintPath>
</Reference> </Reference>
<Reference Include="System.Web.Extensions" /> <Reference Include="System.Web.Extensions" />
<Reference Include="System.Xaml" /> <Reference Include="System.Xaml" />
@ -128,6 +148,9 @@
<HintPath>..\packages\System.Xml.ReaderWriter.4.3.1\lib\net46\System.Xml.ReaderWriter.dll</HintPath> <HintPath>..\packages\System.Xml.ReaderWriter.4.3.1\lib\net46\System.Xml.ReaderWriter.dll</HintPath>
</Reference> </Reference>
<Reference Include="WindowsBase" /> <Reference Include="WindowsBase" />
<Reference Include="ZstdNet, Version=1.4.5.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\packages\ZstdNet.1.4.5\lib\net45\ZstdNet.dll</HintPath>
</Reference>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Compile Include="Artemis\ImageIPT.cs" /> <Compile Include="Artemis\ImageIPT.cs" />
@ -140,6 +163,7 @@
<Compile Include="RPGMaker\AudioRPGMV.cs" /> <Compile Include="RPGMaker\AudioRPGMV.cs" />
<Compile Include="RPGMaker\ImageRPGMV.cs" /> <Compile Include="RPGMaker\ImageRPGMV.cs" />
<Compile Include="Artemis\ShiftReduceParserCode.cs" /> <Compile Include="Artemis\ShiftReduceParserCode.cs" />
<Compile Include="SakanaGL\ArcSX.cs" />
<Compile Include="WebP\ImageWEBP.cs" /> <Compile Include="WebP\ImageWEBP.cs" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
@ -188,6 +212,16 @@
</None> </None>
<None Include="Artemis\IPT.parser" /> <None Include="Artemis\IPT.parser" />
</ItemGroup> </ItemGroup>
<ItemGroup>
<None Include="x64\libzstd.dll">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
</ItemGroup>
<ItemGroup>
<None Include="x86\libzstd.dll">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
</ItemGroup>
<PropertyGroup Label="YltParsers"> <PropertyGroup Label="YltParsers">
<Names>ipt</Names> <Names>ipt</Names>
</PropertyGroup> </PropertyGroup>
@ -204,6 +238,15 @@ exit 0</PreBuildEvent>
<PropertyGroup Label="GenerateIPTProperties"> <PropertyGroup Label="GenerateIPTProperties">
<IPTParser>$(ProjectDir)Artemis\IPT</IPTParser> <IPTParser>$(ProjectDir)Artemis\IPT</IPTParser>
</PropertyGroup> </PropertyGroup>
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
<PropertyGroup>
<ErrorText>This project references NuGet package(s) that are missing on this computer. Enable NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
</PropertyGroup>
<Error Condition="!Exists('$(SolutionDir)\.nuget\NuGet.targets')" Text="$([System.String]::Format('$(ErrorText)', '$(SolutionDir)\.nuget\NuGet.targets'))" />
<Error Condition="!Exists('..\packages\ZstdNet.1.4.5\build\ZstdNet.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\ZstdNet.1.4.5\build\ZstdNet.targets'))" />
<Error Condition="!Exists('..\packages\Stub.System.Data.SQLite.Core.NetFramework.1.0.115.5\build\net46\Stub.System.Data.SQLite.Core.NetFramework.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Stub.System.Data.SQLite.Core.NetFramework.1.0.115.5\build\net46\Stub.System.Data.SQLite.Core.NetFramework.targets'))" />
</Target>
<Import Project="$(SolutionDir)\.nuget\NuGet.targets" Condition="Exists('$(SolutionDir)\.nuget\NuGet.targets')" />
<Target Name="BeforeBuild" DependsOnTargets="YltBuildGen" /> <Target Name="BeforeBuild" DependsOnTargets="YltBuildGen" />
<Target Name="YltBuildGen" DependsOnTargets="GenerateIPT" /> <Target Name="YltBuildGen" DependsOnTargets="GenerateIPT" />
<Target Name="GenerateIPT" Inputs="$(IPTParser).Language.analyzer.lex;$(IPTParser).Language.grammar.y" Outputs="$(IPTParser).Scanner.Generated.cs;$(IPTParser).Parser.Generated.cs"> <Target Name="GenerateIPT" Inputs="$(IPTParser).Language.analyzer.lex;$(IPTParser).Language.grammar.y" Outputs="$(IPTParser).Scanner.Generated.cs;$(IPTParser).Parser.Generated.cs">
@ -224,6 +267,7 @@ exit 0</PreBuildEvent>
<Error Condition="!Exists('..\packages\NETStandard.Library.2.0.3\build\netstandard2.0\NETStandard.Library.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\NETStandard.Library.2.0.3\build\netstandard2.0\NETStandard.Library.targets'))" /> <Error Condition="!Exists('..\packages\NETStandard.Library.2.0.3\build\netstandard2.0\NETStandard.Library.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\NETStandard.Library.2.0.3\build\netstandard2.0\NETStandard.Library.targets'))" />
<Error Condition="!Exists('..\packages\Stub.System.Data.SQLite.Core.NetFramework.1.0.115.5\build\net46\Stub.System.Data.SQLite.Core.NetFramework.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Stub.System.Data.SQLite.Core.NetFramework.1.0.115.5\build\net46\Stub.System.Data.SQLite.Core.NetFramework.targets'))" /> <Error Condition="!Exists('..\packages\Stub.System.Data.SQLite.Core.NetFramework.1.0.115.5\build\net46\Stub.System.Data.SQLite.Core.NetFramework.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Stub.System.Data.SQLite.Core.NetFramework.1.0.115.5\build\net46\Stub.System.Data.SQLite.Core.NetFramework.targets'))" />
</Target> </Target>
<Import Project="..\packages\ZstdNet.1.4.5\build\ZstdNet.targets" Condition="Exists('..\packages\ZstdNet.1.4.5\build\ZstdNet.targets')" />
<Import Project="..\packages\Stub.System.Data.SQLite.Core.NetFramework.1.0.115.5\build\net46\Stub.System.Data.SQLite.Core.NetFramework.targets" Condition="Exists('..\packages\Stub.System.Data.SQLite.Core.NetFramework.1.0.115.5\build\net46\Stub.System.Data.SQLite.Core.NetFramework.targets')" /> <Import Project="..\packages\Stub.System.Data.SQLite.Core.NetFramework.1.0.115.5\build\net46\Stub.System.Data.SQLite.Core.NetFramework.targets" Condition="Exists('..\packages\Stub.System.Data.SQLite.Core.NetFramework.1.0.115.5\build\net46\Stub.System.Data.SQLite.Core.NetFramework.targets')" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it. <!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets. Other similar extension points exist, see Microsoft.Common.targets.

View File

@ -32,5 +32,5 @@ using System.Runtime.InteropServices;
// You can specify all the values or you can default the Build and Revision Numbers // You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below: // by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")] // [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion ("1.0.16.39")] [assembly: AssemblyVersion ("1.0.16.40")]
[assembly: AssemblyFileVersion ("1.0.16.39")] [assembly: AssemblyFileVersion ("1.0.16.40")]

View File

@ -0,0 +1,226 @@
//! \file ArcSX.cs
//! \date 2022 Apr 29
//! \brief SakanaGL resource archive implementation.
//
// Copyright (C) 2022 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.Sakana
{
internal class SxEntry : PackedEntry
{
public ushort Flags;
public bool IsEncrypted { get { return 0 == (Flags & 0x10); } }
}
[Export(typeof(ArchiveFormat))]
public class SxOpener : ArchiveFormat
{
public override string Tag { get { return "SXSTORAGE"; } }
public override string Description { get { return "SakanaGL engine resource archive"; } }
public override uint Signature { get { return 0; } }
public override bool IsHierarchic { get { return true; } }
public override bool CanWrite { get { return false; } }
const uint DefaultKey = 0x2E76034B;
public override ArcFile TryOpen (ArcView file)
{
var base_name = Path.GetFileName (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) || file.Name.Equals (sx_name, StringComparison.InvariantCultureIgnoreCase))
return null;
byte[] index_data;
using (var sx = VFS.OpenView (sx_name))
{
if (sx.MaxOffset <= 0x10)
return null;
if (!sx.View.AsciiEqual (0, "SSXXDEFL"))
return null;
int key = Binary.BigEndian (sx.View.ReadInt32 (8));
int length = (int)(sx.MaxOffset - 0x10);
var index_packed = sx.View.ReadBytes (0x10, (uint)length);
long lkey = (long)key + length;
lkey = key ^ (961 * lkey - 124789) ^ DefaultKey;
uint key_lo = (uint)lkey;
uint key_hi = (uint)(lkey >> 32) ^ 0x2E6;
DecryptData (index_packed, key_lo, key_hi);
index_data = UnpackZstd (index_packed);
}
using (var index = new BinMemoryStream (index_data))
{
var reader = new SxIndexDeserializer (index, file.MaxOffset);
var dir = reader.Deserialize();
return new ArcFile (file, this, dir);
}
}
public override Stream OpenEntry (ArcFile arc, Entry entry)
{
var sx_entry = entry as SxEntry;
if (null == sx_entry || (!sx_entry.IsEncrypted && !sx_entry.IsPacked))
return base.OpenEntry (arc, entry);
var input = arc.File.View.ReadBytes (entry.Offset, entry.Size);
if (sx_entry.IsEncrypted)
{
uint key_lo = (uint)(entry.Offset >> 4) ^ (entry.Size << 16) ^ DefaultKey;
uint key_hi = (entry.Size >> 16) ^ 0x2E6;
DecryptData (input, key_lo, key_hi);
}
if (sx_entry.IsPacked)
input = UnpackZstd (input);
return new BinMemoryStream (input, entry.Name);
}
internal static byte[] UnpackZstd (byte[] data)
{
int unpacked_size = BigEndian.ToInt32 (data, 0);
using (var dec = new ZstdNet.Decompressor())
{
var packed = new ArraySegment<byte> (data, 4, data.Length - 4);
return dec.Unwrap (packed, unpacked_size);
}
}
internal static void DecryptData (byte[] data, uint key_lo, uint key_hi)
{
if (data.Length < 4)
return;
key_lo ^= 0x159A55E5;
key_hi ^= 0x075BCD15;
uint v1 = key_hi ^ (key_hi << 11) ^ ((key_hi ^ (key_hi << 11)) >> 8) ^ 0x549139A;
uint v2 = v1 ^ key_lo ^ (key_lo << 11) ^ ((key_lo ^ (key_lo << 11) ^ (v1 >> 11)) >> 8);
uint v3 = v2 ^ (v2 >> 19) ^ 0x8E415C26;
uint v4 = v3 ^ (v3 >> 19) ^ 0x4D9D5BB8;
int count = data.Length / 4;
unsafe
{
fixed (byte* data_raw = data)
{
uint* data32 = (uint*)&data_raw[0];
for (int i = 0; i < count; ++i)
{
uint t1 = v4 ^ v1 ^ (v1 << 11) ^ ((v1 ^ (v1 << 11) ^ (v4 >> 11)) >> 8);
uint t2 = v2 ^ (v2 << 11);
v2 = v4;
v4 = t1 ^ t2 ^ ((t2 ^ (t1 >> 11)) >> 8);
data32[i] ^= (t1 >> 4) ^ (v4 << 12);
v1 = v3;
v3 = t1;
}
}
}
}
}
internal class SxIndexDeserializer
{
IBinaryStream m_index;
long m_max_offset;
string[] m_name_list;
List<Entry> m_dir;
public SxIndexDeserializer (IBinaryStream index, long max_offset)
{
m_index = index;
m_max_offset = max_offset;
}
public List<Entry> Deserialize ()
{
m_index.Position = 8;
int count = Binary.BigEndian (m_index.ReadInt32());
m_name_list = new string[count];
for (int i = 0; i < count; ++i)
{
int length = m_index.ReadUInt8();
m_name_list[i] = m_index.ReadCString (length, Encoding.UTF8);
}
count = Binary.BigEndian (m_index.ReadInt32());
m_dir = new List<Entry> (count);
for (int i = 0; i < count; ++i)
{
m_index.ReadUInt16();
ushort flags = Binary.BigEndian (m_index.ReadUInt16());
uint offset = Binary.BigEndian (m_index.ReadUInt32());
uint size = Binary.BigEndian (m_index.ReadUInt32());
var entry = new SxEntry {
Flags = flags,
Offset = (long)offset << 4,
Size = size,
IsPacked = 0 != (flags & 0x03),
};
if (!entry.CheckPlacement (m_max_offset))
return null;
m_dir.Add (entry);
}
count = Binary.BigEndian (m_index.ReadUInt16());
for (int i = 0; i < count; ++i)
{
m_index.ReadUInt32();
m_index.ReadUInt32();
m_index.ReadUInt32();
Binary.BigEndian (m_index.ReadUInt32()); // archive body length
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();
return m_dir;
}
void DeserializeTree (string path = "")
{
int count = Binary.BigEndian (m_index.ReadUInt16());
int name_index = Binary.BigEndian (m_index.ReadInt32());
int file_index = Binary.BigEndian (m_index.ReadInt32());
var name = Path.Combine (path, m_name_list[name_index]);
if (-1 == file_index)
{
for (int i = 0; i < count; ++i)
{
DeserializeTree (name);
}
}
else
{
m_dir[file_index].Name = name;
m_dir[file_index].Type = FormatCatalog.Instance.GetTypeFromName (name);
}
}
}
}

View File

@ -8,8 +8,8 @@
</dependentAssembly> </dependentAssembly>
<dependentAssembly> <dependentAssembly>
<assemblyIdentity name="System.Runtime.CompilerServices.Unsafe" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" /> <assemblyIdentity name="System.Runtime.CompilerServices.Unsafe" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-6.0.0.0" newVersion="6.0.0.0" /> <bindingRedirect oldVersion="0.0.0.0-4.0.5.0" newVersion="4.0.5.0" />
</dependentAssembly> </dependentAssembly>
</assemblyBinding> </assemblyBinding>
</runtime> </runtime>
<startup><supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.6.1" /></startup></configuration> </configuration>

View File

@ -1,27 +1,31 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<packages> <packages>
<package id="Concentus" version="1.1.7" targetFramework="net461" /> <package id="Concentus" version="1.1.7" targetFramework="net46" />
<package id="Concentus.Oggfile" version="1.0.4" targetFramework="net461" /> <package id="Concentus.Oggfile" version="1.0.4" targetFramework="net46" />
<package id="Microsoft.NETCore.Platforms" version="6.0.1" targetFramework="net461" /> <package id="Microsoft.NETCore.Platforms" version="6.0.3" targetFramework="net46" />
<package id="Microsoft.Win32.Primitives" version="4.3.0" targetFramework="net461" /> <package id="Microsoft.Win32.Primitives" version="4.3.0" targetFramework="net46" />
<package id="MSFTCompressionCab" version="1.0.0" targetFramework="net461" /> <package id="MSFTCompressionCab" version="1.0.0" targetFramework="net46" />
<package id="NETStandard.Library" version="2.0.3" targetFramework="net461" /> <package id="NETStandard.Library" version="2.0.3" targetFramework="net46" />
<package id="Stub.System.Data.SQLite.Core.NetFramework" version="1.0.115.5" targetFramework="net461" /> <package id="Stub.System.Data.SQLite.Core.NetFramework" version="1.0.115.5" targetFramework="net46" />
<package id="System.AppContext" version="4.3.0" targetFramework="net461" /> <package id="System.AppContext" version="4.3.0" targetFramework="net46" />
<package id="System.Console" version="4.3.1" targetFramework="net461" /> <package id="System.Buffers" version="4.5.1" targetFramework="net46" />
<package id="System.Data.SQLite.Core" version="1.0.115.5" targetFramework="net461" /> <package id="System.Console" version="4.3.1" targetFramework="net46" />
<package id="System.Globalization.Calendars" version="4.3.0" targetFramework="net461" /> <package id="System.Data.SQLite.Core" version="1.0.115.5" targetFramework="net46" />
<package id="System.IO.Compression" version="4.3.0" targetFramework="net461" /> <package id="System.Globalization.Calendars" version="4.3.0" targetFramework="net46" />
<package id="System.IO.Compression.ZipFile" version="4.3.0" targetFramework="net461" /> <package id="System.IO.Compression" version="4.3.0" targetFramework="net46" />
<package id="System.IO.FileSystem" version="4.3.0" targetFramework="net461" /> <package id="System.IO.Compression.ZipFile" version="4.3.0" targetFramework="net46" />
<package id="System.IO.FileSystem.Primitives" version="4.3.0" targetFramework="net461" /> <package id="System.IO.FileSystem" version="4.3.0" targetFramework="net46" />
<package id="System.Net.Http" version="4.3.4" targetFramework="net461" /> <package id="System.IO.FileSystem.Primitives" version="4.3.0" targetFramework="net46" />
<package id="System.Net.Sockets" version="4.3.0" targetFramework="net461" /> <package id="System.Memory" version="4.5.4" targetFramework="net46" />
<package id="System.Runtime.InteropServices.RuntimeInformation" version="4.3.0" targetFramework="net461" /> <package id="System.Net.Http" version="4.3.4" targetFramework="net46" />
<package id="System.Security.Cryptography.Algorithms" version="4.3.1" targetFramework="net461" /> <package id="System.Net.Sockets" version="4.3.0" targetFramework="net46" />
<package id="System.Security.Cryptography.Encoding" version="4.3.0" targetFramework="net461" /> <package id="System.Runtime.CompilerServices.Unsafe" version="4.6.0" targetFramework="net46" />
<package id="System.Security.Cryptography.Primitives" version="4.3.0" targetFramework="net461" /> <package id="System.Runtime.InteropServices.RuntimeInformation" version="4.3.0" targetFramework="net46" />
<package id="System.Security.Cryptography.X509Certificates" version="4.3.2" targetFramework="net461" /> <package id="System.Security.Cryptography.Algorithms" version="4.3.1" targetFramework="net46" />
<package id="System.Xml.ReaderWriter" version="4.3.1" targetFramework="net461" /> <package id="System.Security.Cryptography.Encoding" version="4.3.0" targetFramework="net46" />
<package id="YaccLexTools" version="0.2.2" targetFramework="net461" /> <package id="System.Security.Cryptography.Primitives" version="4.3.0" targetFramework="net46" />
<package id="System.Security.Cryptography.X509Certificates" version="4.3.2" targetFramework="net46" />
<package id="System.Xml.ReaderWriter" version="4.3.1" targetFramework="net46" />
<package id="YaccLexTools" version="0.2.2" targetFramework="net46" />
<package id="ZstdNet" version="1.4.5" targetFramework="net46" />
</packages> </packages>

View File

@ -228,24 +228,39 @@ namespace GARbro.GUI
var src_format = ImageFormat.FindFormat (file); var src_format = ImageFormat.FindFormat (file);
if (null == src_format) if (null == src_format)
return; return;
if (src_format.Item1 == m_image_format && m_image_format.Extensions.Any (ext => ext == source_ext)) Stream output = null;
return;
file.Position = 0;
var image = src_format.Item1.Read (file, src_format.Item2);
var output = CreateNewFile (target_name);
try try
{ {
m_image_format.Write (output, image); if (src_format.Item1 == m_image_format && m_image_format.Extensions.Any (ext => ext == source_ext))
{
if (AreSamePaths (filename, target_name))
return;
output = CreateNewFile (target_name);
file.Position = 0;
file.AsStream.CopyTo (output);
}
else
{
file.Position = 0;
var image = src_format.Item1.Read (file, src_format.Item2);
output = CreateNewFile (target_name);
m_image_format.Write (output, image);
}
} }
catch // delete destination file on conversion failure catch // delete destination file on conversion failure
{ {
// FIXME if user chooses to overwrite file, and conversion results in error, // FIXME if user chooses to overwrite file, and conversion results in error,
// then original file will be lost. // then original file will be lost.
output.Dispose(); output.Dispose();
output = null;
File.Delete (target_name); File.Delete (target_name);
throw; throw;
} }
output.Dispose(); finally
{
if (output != null)
output.Dispose();
}
} }
} }
@ -264,5 +279,12 @@ namespace GARbro.GUI
m_main.ListViewFocus(); m_main.ListViewFocus();
m_main.RefreshView(); m_main.RefreshView();
} }
static internal bool AreSamePaths (string filename1, string filename2)
{
filename1 = Path.GetFullPath (filename1);
filename2 = Path.GetFullPath (filename2);
return string.Equals (filename1, filename2, StringComparison.OrdinalIgnoreCase);
}
} }
} }

View File

@ -240,7 +240,7 @@ namespace GARbro.GUI
{ {
using (var data = VFS.OpenImage (preview.Entry)) using (var data = VFS.OpenImage (preview.Entry))
{ {
SetPreviewImage (preview, data.Image.Bitmap); SetPreviewImage (preview, data.Image.Bitmap, data.SourceFormat);
} }
} }
catch (Exception X) catch (Exception X)
@ -250,7 +250,7 @@ namespace GARbro.GUI
} }
} }
void SetPreviewImage (PreviewFile preview, BitmapSource bitmap) void SetPreviewImage (PreviewFile preview, BitmapSource bitmap, ImageFormat format)
{ {
if (bitmap.DpiX != Desktop.DpiX || bitmap.DpiY != Desktop.DpiY) if (bitmap.DpiX != Desktop.DpiX || bitmap.DpiY != Desktop.DpiY)
{ {
@ -271,7 +271,7 @@ namespace GARbro.GUI
ImageCanvas.Source = bitmap; ImageCanvas.Source = bitmap;
ApplyDownScaleSetting(); ApplyDownScaleSetting();
SetStatusText (string.Format (guiStrings.MsgImageSize, bitmap.PixelWidth, SetStatusText (string.Format (guiStrings.MsgImageSize, bitmap.PixelWidth,
bitmap.PixelHeight, bitmap.Format.BitsPerPixel)); bitmap.PixelHeight, bitmap.Format.BitsPerPixel, format?.Tag ?? "?"));
} }
}); });
} }

View File

@ -1133,13 +1133,13 @@ namespace GARbro.GUI
try try
{ {
var file_list = items.Select (entry => Path.Combine (CurrentPath, entry.Name)); var file_list = items.Select (entry => Path.Combine (CurrentPath, entry.Name));
GARbro.Shell.File.Delete (file_list); if (!GARbro.Shell.File.Delete (file_list, new WindowInteropHelper(this).Handle))
throw new ApplicationException ("Delete operation failed.");
count = file_list.Count(); count = file_list.Count();
} }
catch finally
{ {
ResumeWatchDirectoryChanges(); ResumeWatchDirectoryChanges();
throw;
} }
RefreshView(); RefreshView();
SetStatusText (Localization.Format ("MsgDeletedItems", count)); SetStatusText (Localization.Format ("MsgDeletedItems", count));

View File

@ -51,5 +51,5 @@ using System.Windows;
// You can specify all the values or you can default the Build and Revision Numbers // You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below: // by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")] // [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion ("1.5.44.2904")] [assembly: AssemblyVersion ("1.5.44.2938")]
[assembly: AssemblyFileVersion ("1.5.44.2904")] [assembly: AssemblyFileVersion ("1.5.44.2938")]

View File

@ -119,42 +119,86 @@ namespace GARbro.Shell
/// <summary> /// <summary>
/// SHFILEOPSTRUCT for SHFileOperation from COM /// SHFILEOPSTRUCT for SHFileOperation from COM
/// </summary> /// </summary>
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto, Pack = 1)] [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode, Pack = 1)]
private struct SHFILEOPSTRUCT private struct SHFILEOPSTRUCT32
{ {
public IntPtr hwnd; public IntPtr hwnd;
[MarshalAs(UnmanagedType.U4)] [MarshalAs(UnmanagedType.U4)]
public FileOperationType wFunc; public FileOperationType wFunc;
[MarshalAs(UnmanagedType.LPTStr)]
public string pFrom; public string pFrom;
[MarshalAs(UnmanagedType.LPTStr)]
public string pTo; public string pTo;
public FileOperationFlags fFlags; public FileOperationFlags fFlags;
[MarshalAs(UnmanagedType.Bool)] [MarshalAs(UnmanagedType.Bool)]
public bool fAnyOperationsAborted; public bool fAnyOperationsAborted;
public IntPtr hNameMappings; public IntPtr hNameMappings;
[MarshalAs(UnmanagedType.LPTStr)]
public string lpszProgressTitle; public string lpszProgressTitle;
} }
[DllImport("shell32.dll", CharSet = CharSet.Auto)] [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
private static extern int SHFileOperation (ref SHFILEOPSTRUCT FileOp); public struct SHFILEOPSTRUCT64
{
public IntPtr hwnd;
[MarshalAs(UnmanagedType.U4)]
public FileOperationType wFunc;
[MarshalAs(UnmanagedType.LPTStr)]
public string pFrom;
[MarshalAs(UnmanagedType.LPTStr)]
public string pTo;
public FileOperationFlags fFlags;
public bool fAnyOperationsAborted;
public IntPtr hNameMappings;
[MarshalAs(UnmanagedType.LPTStr)]
public string lpszProgressTitle;
}
[DllImport("shell32.dll", EntryPoint = "SHFileOperationW", CharSet = CharSet.Unicode)]
private static extern int SHFileOperation32 (ref SHFILEOPSTRUCT32 FileOp);
[DllImport("shell32.dll", EntryPoint = "SHFileOperationW", CharSet = CharSet.Unicode)]
private static extern int SHFileOperation64 (ref SHFILEOPSTRUCT64 lpFileOp);
private static int SHFileOperation (FileOperationType func, string path, FileOperationFlags flags, IntPtr parent)
{
if (Marshal.SizeOf(typeof(IntPtr)) == 4)
{
var fs = new SHFILEOPSTRUCT32
{
hwnd = parent,
wFunc = func,
pFrom = path,
fFlags = flags
};
return SHFileOperation32 (ref fs);
}
else
{
var fs = new SHFILEOPSTRUCT64
{
hwnd = parent,
wFunc = func,
pFrom = path,
fFlags = flags
};
return SHFileOperation64 (ref fs);
}
}
/// <summary> /// <summary>
/// Send file to recycle bin /// Send file to recycle bin
/// </summary> /// </summary>
/// <param name="path">Location of directory or file to recycle</param> /// <param name="path">Location of directory or file to recycle</param>
/// <param name="flags">FileOperationFlags to add in addition to FOF_ALLOWUNDO</param> /// <param name="flags">FileOperationFlags to add in addition to FOF_ALLOWUNDO</param>
public static bool Delete (string path, FileOperationFlags flags) public static bool Delete (string path, FileOperationFlags flags, IntPtr parent = default(IntPtr))
{ {
var fs = new SHFILEOPSTRUCT return 0 == SHFileOperation (FileOperationType.FO_DELETE, path+'\0'+'\0',
{ FileOperationFlags.FOF_ALLOWUNDO | flags, parent);
wFunc = FileOperationType.FO_DELETE,
pFrom = path + '\0' + '\0',
fFlags = FileOperationFlags.FOF_ALLOWUNDO | flags
};
return 0 == SHFileOperation (ref fs);
} }
public static bool Delete (IEnumerable<string> file_list, FileOperationFlags flags) public static bool Delete (IEnumerable<string> file_list, FileOperationFlags flags, IntPtr parent = default(IntPtr))
{ {
var files = new StringBuilder(); var files = new StringBuilder();
foreach (var file in file_list) foreach (var file in file_list)
@ -165,36 +209,31 @@ namespace GARbro.Shell
if (0 == files.Length) if (0 == files.Length)
return false; return false;
files.Append ('\0'); files.Append ('\0');
var fs = new SHFILEOPSTRUCT return 0 == SHFileOperation (FileOperationType.FO_DELETE, files.ToString(),
{ FileOperationFlags.FOF_ALLOWUNDO | flags, parent);
wFunc = FileOperationType.FO_DELETE,
pFrom = files.ToString(),
fFlags = FileOperationFlags.FOF_ALLOWUNDO | flags
};
return 0 == SHFileOperation (ref fs);
} }
public static bool Delete (IEnumerable<string> file_list) public static bool Delete (IEnumerable<string> file_list, IntPtr parent = default(IntPtr))
{ {
return Delete (file_list, FileOperationFlags.FOF_WANTNUKEWARNING); return Delete (file_list, FileOperationFlags.FOF_WANTNUKEWARNING, parent);
} }
/// <summary> /// <summary>
/// Send file to recycle bin. Display dialog, display warning if files are too big to fit (FOF_WANTNUKEWARNING) /// Send file to recycle bin. Display dialog, display warning if files are too big to fit (FOF_WANTNUKEWARNING)
/// </summary> /// </summary>
/// <param name="path">Location of directory or file to recycle</param> /// <param name="path">Location of directory or file to recycle</param>
public static bool Delete (string path) public static bool Delete (string path, IntPtr parent = default(IntPtr))
{ {
return Delete (path, FileOperationFlags.FOF_NOCONFIRMATION | FileOperationFlags.FOF_WANTNUKEWARNING); return Delete (path, FileOperationFlags.FOF_NOCONFIRMATION | FileOperationFlags.FOF_WANTNUKEWARNING, parent);
} }
/// <summary> /// <summary>
/// Send file silently to recycle bin. Surpress dialog, surpress errors, delete if too large. /// Send file silently to recycle bin. Surpress dialog, surpress errors, delete if too large.
/// </summary> /// </summary>
/// <param name="path">Location of directory or file to recycle</param> /// <param name="path">Location of directory or file to recycle</param>
public static bool MoveToRecycleBin (string path) public static bool MoveToRecycleBin (string path, IntPtr parent = default(IntPtr))
{ {
return Delete (path, FileOperationFlags.FOF_NOCONFIRMATION | FileOperationFlags.FOF_NOERRORUI | FileOperationFlags.FOF_SILENT); return Delete (path, FileOperationFlags.FOF_NOCONFIRMATION | FileOperationFlags.FOF_NOERRORUI | FileOperationFlags.FOF_SILENT, parent);
} }

View File

@ -270,7 +270,7 @@
<comment>Extracting files from {0} to {1}</comment> <comment>Extracting files from {0} to {1}</comment>
</data> </data>
<data name="MsgImageSize" xml:space="preserve"> <data name="MsgImageSize" xml:space="preserve">
<value>イメージ {0} x {1} x {2}bpp</value> <value>イメージ {0} x {1} x {2}bpp [{3}]</value>
<comment>Image {0} x {1} x {2}bpp</comment> <comment>Image {0} x {1} x {2}bpp</comment>
</data> </data>
<data name="MsgNoFiles" xml:space="preserve"> <data name="MsgNoFiles" xml:space="preserve">

View File

@ -232,7 +232,7 @@
<value>{0}에서 {1}로 파일 추출하기</value> <value>{0}에서 {1}로 파일 추출하기</value>
</data> </data>
<data name="MsgImageSize" xml:space="preserve"> <data name="MsgImageSize" xml:space="preserve">
<value>이미지 {0} x {1} x {2}bpp</value> <value>이미지 {0} x {1} x {2}bpp [{3}]</value>
</data> </data>
<data name="MsgNoFiles" xml:space="preserve"> <data name="MsgNoFiles" xml:space="preserve">
<value>추출할 파일이 없음</value> <value>추출할 파일이 없음</value>

View File

@ -232,7 +232,7 @@
<value>Extracting files from {0} to {1}</value> <value>Extracting files from {0} to {1}</value>
</data> </data>
<data name="MsgImageSize" xml:space="preserve"> <data name="MsgImageSize" xml:space="preserve">
<value>Image {0} x {1} x {2}bpp</value> <value>Image {0} x {1} x {2}bpp [{3}]</value>
</data> </data>
<data name="MsgNoFiles" xml:space="preserve"> <data name="MsgNoFiles" xml:space="preserve">
<value>no files to extract</value> <value>no files to extract</value>

View File

@ -226,7 +226,7 @@
<value>Извлекаются файлы из {0} в {1}</value> <value>Извлекаются файлы из {0} в {1}</value>
</data> </data>
<data name="MsgImageSize" xml:space="preserve"> <data name="MsgImageSize" xml:space="preserve">
<value>Изображение {0} x {1} x {2}bpp</value> <value>Изображение {0} x {1} x {2}bpp [{3}]</value>
</data> </data>
<data name="MsgNoFiles" xml:space="preserve"> <data name="MsgNoFiles" xml:space="preserve">
<value>отсутствуют файлы, удовлетворяющие выбранным критериям</value> <value>отсутствуют файлы, удовлетворяющие выбранным критериям</value>

View File

@ -232,7 +232,7 @@
<value>正在从{0}中提取文件至{1}……</value> <value>正在从{0}中提取文件至{1}……</value>
</data> </data>
<data name="MsgImageSize" xml:space="preserve"> <data name="MsgImageSize" xml:space="preserve">
<value>图像 {0} x {1} x {2}bpp</value> <value>图像 {0} x {1} x {2}bpp [{3}]</value>
</data> </data>
<data name="MsgNoFiles" xml:space="preserve"> <data name="MsgNoFiles" xml:space="preserve">
<value>没有可以提取的文件。</value> <value>没有可以提取的文件。</value>

View File

@ -1,7 +1,7 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<packages> <packages>
<package id="NAudio" version="1.10.0" targetFramework="net461" /> <package id="NAudio" version="1.10.0" targetFramework="net46" />
<package id="WindowsAPICodePack-Core" version="1.1.2" targetFramework="net461" /> <package id="WindowsAPICodePack-Core" version="1.1.2" targetFramework="net46" />
<package id="WindowsAPICodePack-Shell" version="1.1.1" targetFramework="net461" /> <package id="WindowsAPICodePack-Shell" version="1.1.1" targetFramework="net46" />
<package id="WPFToolkit" version="3.5.50211.1" targetFramework="net461" /> <package id="WPFToolkit" version="3.5.50211.1" targetFramework="net46" />
</packages> </packages>

View File

@ -32,5 +32,5 @@ using System.Runtime.InteropServices;
// You can specify all the values or you can default the Build and Revision Numbers // You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below: // by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")] // [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion ("1.5.44.319")] [assembly: AssemblyVersion ("1.5.44.320")]
[assembly: AssemblyFileVersion ("1.5.44.319")] [assembly: AssemblyFileVersion ("1.5.44.320")]

View File

@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<packages> <packages>
<package id="NAudio" version="1.10.0" targetFramework="net461" /> <package id="NAudio" version="1.10.0" targetFramework="net46" />
</packages> </packages>

75
Legacy/Gsx/ImageK4.cs Normal file
View File

@ -0,0 +1,75 @@
//! \file ImageK4.cs
//! \date 2019 Feb 07
//! \brief Toyo GSX image format.
//
// Copyright (C) 2019 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;
// [030825][Mirai] Hoshi no Oujo
namespace GameRes.Formats.Gsx
{
internal class K4MetaData : ImageMetaData
{
public bool HasAlpha;
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 ImageMetaData ReadMetaData (IBinaryStream file)
{
var header = file.ReadHeader (0x10);
if (!header.AsciiEqual ("K4"))
return null;
if (header[2] != 1 || header[3] != 2)
return null;
return new K4MetaData {
Width = header.ToUInt16 (4),
Height = header.ToUInt16 (6),
BPP = header[0xF],
HasAlpha = header[0xB] != 0,
FrameCount = header.ToUInt16 (0xC),
};
}
public override ImageData Read (IBinaryStream file, ImageMetaData info)
{
var meta = (K4MetaData)info;
return ImageData.Create (info, format, palette, pixels);
}
public override void Write (Stream file, ImageData image)
{
throw new System.NotImplementedException ("K4Format.Write not implemented");
}
}
}

View File

@ -0,0 +1,62 @@
//! \file ImageI24.cs
//! \date 2019 Jun 22
//! \brief HyperWorks image format.
//
// Copyright (C) 2019 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.HyperWorks
{
[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 ImageMetaData ReadMetaData (IBinaryStream file)
{
var header = file.ReadHeader (0x18);
int bpp = header.ToInt16 (0x10);
if (bpp != 24)
return null;
return new ImageMetaData {
Width = header.ToUInt16 (0xC),
Height = header.ToUInt16 (0xE),
BPP = bpp,
};
}
public override ImageData Read (IBinaryStream file, ImageMetaData info)
{
return ImageData.Create (info, format, palette, pixels);
}
public override void Write (Stream file, ImageData image)
{
throw new System.NotImplementedException ("I24Format.Write not implemented");
}
}
}

View File

@ -39,10 +39,17 @@ namespace GameRes.Formats.KApp
public override string Description { get { return "KApp compressed image format"; } } public override string Description { get { return "KApp compressed image format"; } }
public override uint Signature { get { return 0x6F6F746B; } } // 'ktool210' public override uint Signature { get { return 0x6F6F746B; } } // 'ktool210'
public CgdFormat ()
{
Signatures = new uint[] { 0x6F6F746B, 0x65697073 };
}
public override ImageMetaData ReadMetaData (IBinaryStream file) public override ImageMetaData ReadMetaData (IBinaryStream file)
{ {
var header = file.ReadHeader (0x18); var header = file.ReadHeader (0x18);
if (!header.AsciiEqual ("ktool210") || header.ToInt32 (8) != 1) if (header.ToInt32 (8) != 1)
return null;
if (!header.AsciiEqual ("ktool210") && !header.AsciiEqual ("spiel100"))
return null; return null;
uint offset = header.ToUInt32 (0x10) & 0x7FFFFFFF; uint offset = header.ToUInt32 (0x10) & 0x7FFFFFFF;
return CgdMetaData.FromStream (file, offset); return CgdMetaData.FromStream (file, offset);

View File

@ -103,6 +103,7 @@
<Compile Include="FazeX\ImageFGP.cs" /> <Compile Include="FazeX\ImageFGP.cs" />
<Compile Include="HillField\ImageIMA.cs" /> <Compile Include="HillField\ImageIMA.cs" />
<Compile Include="HillField\ImageIMG.cs" /> <Compile Include="HillField\ImageIMG.cs" />
<Compile Include="hmp\ImageALP.cs" />
<Compile Include="KApp\ArcASD.cs" /> <Compile Include="KApp\ArcASD.cs" />
<Compile Include="KApp\ImageCGD.cs" /> <Compile Include="KApp\ImageCGD.cs" />
<Compile Include="Kasane\ArcAR2.cs" /> <Compile Include="Kasane\ArcAR2.cs" />
@ -226,6 +227,7 @@
<Compile Include="Nekotaro\ArcNSC.cs" /> <Compile Include="Nekotaro\ArcNSC.cs" />
<Compile Include="Nekotaro\ImageGCmp.cs" /> <Compile Include="Nekotaro\ImageGCmp.cs" />
<Compile Include="PlanTech\ArcPAC.cs" /> <Compile Include="PlanTech\ArcPAC.cs" />
<Compile Include="Zenos\ImagePNX.cs" />
<Compile Include="Zone\ArcPKD.cs" /> <Compile Include="Zone\ArcPKD.cs" />
<Compile Include="Zone\ImageBM_.cs" /> <Compile Include="Zone\ImageBM_.cs" />
<None Include="app.config" /> <None Include="app.config" />

View File

@ -27,6 +27,7 @@ using System;
using System.ComponentModel.Composition; using System.ComponentModel.Composition;
using System.IO; using System.IO;
using System.Windows; using System.Windows;
using System.Windows.Media;
using System.Windows.Media.Imaging; using System.Windows.Media.Imaging;
namespace GameRes.Formats.Pochette namespace GameRes.Formats.Pochette
@ -93,15 +94,21 @@ namespace GameRes.Formats.Pochette
BitmapSource BlendBaseLine (BitmapSource overlay, GdtMetaData meta) BitmapSource BlendBaseLine (BitmapSource overlay, GdtMetaData meta)
{ {
string base_name = VFS.ChangeFileName (meta.FileName, meta.BaseLine+".gdt"); string base_name = VFS.ChangeFileName (meta.FileName, meta.BaseLine);
if (!VFS.FileExists (base_name)) if (!VFS.FileExists (base_name))
return overlay; {
base_name += ".gdt";
if (!VFS.FileExists (base_name))
return overlay;
}
using (var base_file = VFS.OpenBinaryStream (base_name)) using (var base_file = VFS.OpenBinaryStream (base_name))
{ {
var base_info = ReadMetaData (base_file) as GdtMetaData; var base_info = ReadMetaData (base_file) as GdtMetaData;
if (null == base_info) if (null == base_info)
return overlay; return overlay;
var base_image = ReadBitmapSource (base_file, base_info); var base_image = ReadBitmapSource (base_file, base_info);
if (base_image.Format.BitsPerPixel < 24)
base_image = new FormatConvertedBitmap (base_image, PixelFormats.Bgr32, null, 0);
var canvas = new WriteableBitmap (base_image); var canvas = new WriteableBitmap (base_image);
int canvas_bpp = canvas.Format.BitsPerPixel; int canvas_bpp = canvas.Format.BitsPerPixel;
if (canvas_bpp != overlay.Format.BitsPerPixel) if (canvas_bpp != overlay.Format.BitsPerPixel)

View File

@ -32,5 +32,5 @@ using System.Runtime.InteropServices;
// You can specify all the values or you can default the Build and Revision Numbers // You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below: // by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")] // [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion ("1.0.10.196")] [assembly: AssemblyVersion ("1.0.10.198")]
[assembly: AssemblyFileVersion ("1.0.10.196")] [assembly: AssemblyFileVersion ("1.0.10.198")]

64
Legacy/Zenos/ImagePNX.cs Normal file
View File

@ -0,0 +1,64 @@
//! \file ImagePNX.cs
//! \date 2022 Apr 14
//! \brief Obfuscated PNG image.
//
// Copyright (C) 2022 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.Zenos
{
[Export(typeof(ImageFormat))]
public class PnxFormat : PngFormat
{
public override string Tag { get { return "PNX/ZENOS"; } }
public override string Description { get { return "Obfuscated PNG image"; } }
public override uint Signature { get { return 0x584E5089; } }
public override bool CanWrite { get { return false; } }
public override ImageMetaData ReadMetaData (IBinaryStream file)
{
using (var input = DeobfuscateStream (file))
return base.ReadMetaData (input);
}
public override ImageData Read (IBinaryStream file, ImageMetaData info)
{
using (var input = DeobfuscateStream (file))
return base.Read (input, info);
}
IBinaryStream DeobfuscateStream (IBinaryStream file)
{
var body = new StreamRegion (file.AsStream, 8, file.Length-8, true);
var png = new PrefixStream (HeaderBytes, body);
return new BinaryStream (png, file.Name);
}
public override void Write (Stream file, ImageData image)
{
throw new System.NotImplementedException ("PnxFormat.Write not implemented");
}
}
}

View File

@ -8,8 +8,8 @@
</dependentAssembly> </dependentAssembly>
<dependentAssembly> <dependentAssembly>
<assemblyIdentity name="System.Runtime.CompilerServices.Unsafe" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" /> <assemblyIdentity name="System.Runtime.CompilerServices.Unsafe" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-6.0.0.0" newVersion="6.0.0.0" /> <bindingRedirect oldVersion="0.0.0.0-4.0.5.0" newVersion="4.0.5.0" />
</dependentAssembly> </dependentAssembly>
</assemblyBinding> </assemblyBinding>
</runtime> </runtime>
<startup><supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.6.1" /></startup></configuration> </configuration>

60
Legacy/hmp/ImageALP.cs Normal file
View File

@ -0,0 +1,60 @@
//! \file ImageALP.cs
//! \date 2022 Apr 22
//! \brief BeF bitmap mask
//
// Copyright (C) 2022 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.Hmp
{
[Export(typeof(ImageFormat))]
[ExportMetadata("Priority", -1)]
public class AlpFormat : ImageFormat
{
public override string Tag { get { return "ALP/BeF"; } }
public override string Description { get { return "BeF bitmap mask format"; } }
public override uint Signature { get { return 0; } }
public override ImageMetaData ReadMetaData (IBinaryStream file)
{
if (!file.Name.HasExtension (".alp") || file.Length != 0x25800)
return null;
return new ImageMetaData { Width = 320, Height = 480, BPP = 8 };
}
public override ImageData Read (IBinaryStream file, ImageMetaData info)
{
var pixels = file.ReadBytes (0x25800);
for (int i = 0; i < pixels.Length; ++i)
pixels[i] = (byte)(pixels[i] * 0xFF / 0x40);
return ImageData.Create (info, PixelFormats.Gray8, null, pixels);
}
public override void Write (Stream file, ImageData image)
{
throw new System.NotImplementedException ("AlpFormat.Write not implemented");
}
}
}

View File

@ -9,11 +9,7 @@ use File::Spec;
use File::Temp; use File::Temp;
sub get_git_exe { sub get_git_exe {
my $user_app_data = Win32::GetFolderPath (Win32::CSIDL_LOCAL_APPDATA); return "git.exe";
my $git_glob = File::Spec->catfile ($user_app_data, 'GitHub', 'PortableGit_*', 'cmd', 'git.exe');
my $git_path = glob ($git_glob);
die "PortableGit not found\n" unless -x $git_path;
return $git_path;
} }
sub match_version { sub match_version {