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

View File

@ -40,13 +40,13 @@ namespace GameRes.Formats
{
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
{
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 ()
{
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

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;
Tuple<string, int> parsed = null;
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);
}
else // Shukujo no Tsuyagoto special case
parsed = OldDatOpener.ArcNameParser.ParseName (arc_name);
if (null == parsed)
@ -217,7 +220,7 @@ namespace GameRes.Formats.Cyberworks
var toc = ReadToc (toc_name, 8);
if (null == toc)
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())
return null;
@ -311,10 +314,8 @@ namespace GameRes.Formats.Cyberworks
if ('c' == type || 'b' == type)
{
uint img_size = Binary.BigEndian (input.ReadUInt32());
if (input.Length - 5 == img_size)
{
input = BinaryStream.FromStream (new StreamRegion (input.AsStream, 5, img_size), input.Name);
}
long start_pos = input.Length - img_size;
input = BinaryStream.FromStream (new StreamRegion (input.AsStream, start_pos, img_size), input.Name);
}
else if (scheme != null && ('a' == type || 'd' == type) && input.Length > 21)
{
@ -618,9 +619,13 @@ namespace GameRes.Formats.Cyberworks
return true;
}
uint m_fault_id = 100000;
internal PackedEntry ReadEntryInfo ()
{
uint id = m_index.ReadUInt32();
if (id > m_fault_id)
id = m_fault_id++;
var entry = new PackedEntry { Name = id.ToString ("D6") };
entry.UnpackedSize = m_index.ReadUInt32();
entry.Size = m_index.ReadUInt32();
@ -650,10 +655,14 @@ namespace GameRes.Formats.Cyberworks
internal class ArcIndexReader : IndexReader
{
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_game_name = game_name;
m_ignore_b_files = m_game_name == "ドキドキ母娘レッスン ~教えて♪Hなお勉強~";
}
char[] m_type = new char[2];
@ -677,7 +686,7 @@ namespace GameRes.Formats.Cyberworks
ext = new string (m_type);
else
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";
HasImages = true;

View File

@ -2,7 +2,7 @@
//! \date Fri Jun 17 18:49:04 2016
//! \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
// of this software and associated documentation files (the "Software"), to
@ -28,6 +28,7 @@ using System.Collections.Generic;
using System.IO;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using GameRes.Utility;
namespace GameRes.Formats.Cyberworks
{
@ -145,9 +146,17 @@ namespace GameRes.Formats.Cyberworks
{
var size_buf = new byte[4];
input.Read (size_buf, 0 , 4);
var decoder = new PngBitmapDecoder (input, BitmapCreateOptions.None,
BitmapCacheOption.OnLoad);
BitmapSource frame = decoder.Frames[0];
int png_size = BigEndian.ToInt32 (size_buf, 0);
BitmapSource frame;
// 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.Height = (uint)frame.PixelHeight;
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
// 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
// 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
// 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.
//
using System;
using System.Collections.Generic;
using System.ComponentModel.Composition;
using System.IO;
@ -42,60 +41,110 @@ namespace GameRes.Formats.Kaguya
}
}
[Export(typeof(ArchiveFormat))]
public class AnmOpener : ArchiveFormat
internal class AnmEntry : Entry
{
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 uint Signature { get { return 0x30304E41; } } // 'AN00'
public override bool IsHierarchic { get { return false; } }
public override bool CanWrite { get { return false; } }
public AnmOpener ()
public AnmOpenerBase ()
{
Extensions = new string[] { "anm" };
}
public override ArcFile TryOpen (ArcView file)
{
int frame_count = file.View.ReadInt16 (0x14);
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
using (var input = file.CreateStream())
{
OffsetX = file.View.ReadInt32 (4),
OffsetY = file.View.ReadInt32 (8),
Width = file.View.ReadUInt32 (0x0C),
Height = file.View.ReadUInt32 (0x10),
BPP = 32,
};
current_offset += 2;
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
var dir = GetFramesList (input);
if (null == dir)
return null;
var base_info = GetBaseInfo (input);
string base_name = Path.GetFileNameWithoutExtension (file.Name);
int i = 0;
foreach (var entry in dir)
{
Name = string.Format ("{0}#{1:D2}", base_name, i),
Type = "image",
Offset = current_offset,
Size = 0x10 + 4*width*height,
};
dir.Add (entry);
current_offset += entry.Size;
entry.Name = string.Format ("{0}#{1:D2}", base_name, i++);
entry.Type = "image";
}
return new AnmArchive (file, this, dir, base_info);
}
return new AnmArchive (file, this, dir, base_info);
}
public override IImageDecoder OpenImage (ArcFile arc, Entry entry)
{
var base_info = ((AnmArchive)arc).ImageInfo;
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))]
public class An20Opener : ArchiveFormat
public class An10Opener : AnmOpenerBase, IAnmReader
{
public override string Tag { get { return "AN20/KAGUYA"; } }
public override string Description { get { return "KaGuYa script engine animation resource"; } }
public override uint Signature { get { return 0x30324E41; } } // 'AN20'
public override bool IsHierarchic { get { return false; } }
public override bool CanWrite { get { return false; } }
public override string Tag { get { return "AN10/KAGUYA"; } }
public override uint Signature { get { return 0x30314E41; } } // 'AN10'
public An20Opener ()
public override List<Entry> GetFramesList (IBinaryStream file)
{
Extensions = new string[] { "anm" };
}
public override ArcFile TryOpen (ArcView file)
{
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);
file.Position = 0x14;
int frame_count = file.ReadInt16();
file.Position = 0x18 + frame_count * 4;
int count = file.ReadInt16();
if (!IsSaneCount (count))
return null;
current_offset += 2;
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 current_offset = file.Position;
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+0x0C);
uint depth = file.View.ReadUInt32 (current_offset+0x10);
var entry = new Entry
file.Position = current_offset + 8;
uint width = file.ReadUInt32();
uint height = file.ReadUInt32();
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,
Size = 0x14 + depth*width*height,
Size = 0x14 + image_size,
ImageDataOffset = current_offset + 0x14,
ImageDataSize = image_size,
};
dir.Add (entry);
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;
var input = arc.File.CreateStream (entry.Offset, entry.Size);
return new An20Decoder (input, base_info);
return new An10Decoder (input, 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 frame = new byte[0x100];

View File

@ -65,7 +65,7 @@ namespace GameRes.Formats.Kaguya
{
int version = file.View.ReadByte (4) - '0';
if (version < 3 || version > 6)
return null;
return ReadOldIndex (file);
using (var reader = LinkReader.Create (file, version))
{
@ -87,7 +87,22 @@ namespace GameRes.Formats.Kaguya
{
var lent = entry as LinkEntry;
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);
}
if (lent.IsEncrypted)
{
var larc = arc as LinkArchive;
@ -102,6 +117,35 @@ namespace GameRes.Formats.Kaguya
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
@ -440,9 +484,15 @@ namespace GameRes.Formats.Kaguya
var header = input.ReadHeader (0x11);
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)
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))
return new ParamsV5Deserializer (input, version);
}
@ -499,6 +549,22 @@ namespace GameRes.Formats.Kaguya
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
@ -515,17 +581,7 @@ namespace GameRes.Formats.Kaguya
public override byte[] GetKey ()
{
m_input.Position = 0x17;
SkipChunk();
m_title = ReadString();
m_input.ReadCString();
SkipString();
SkipString();
m_input.ReadByte();
SkipString();
SkipString();
SkipDict();
m_input.ReadByte();
ReadHeader (0x17);
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
{
public ParamsV5Deserializer (IBinaryStream input, Version version) : base (input, version)
@ -573,18 +660,9 @@ namespace GameRes.Formats.Kaguya
public override byte[] GetKey ()
{
// ハラミタマ
m_input.Position = 0x1B;
SkipChunk();
m_title = ReadString();
SkipString();
SkipString();
m_input.ReadByte();
SkipString();
SkipString();
SkipDict();
ReadHeader (0x1B);
Skip (m_version.Minor <= 4 ? 0x10 : 0x11);
Skip (m_version.Minor <= 4 ? 15 : 16);
for (int i = 0; i < 3; ++i)
{
if (0 != m_input.ReadUInt8())
@ -633,6 +711,11 @@ namespace GameRes.Formats.Kaguya
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)
{
if (null == key || 0 == key.Length)
@ -675,13 +758,12 @@ namespace GameRes.Formats.Kaguya
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);
int frame_offset = 0x18 + data.ToUInt16 (0x14) * 4;
int count = data.ToUInt16 (frame_offset);
frame_offset += 10;
for (int i = 0; i < count; ++i)
var input = new BinMemoryStream (data, entry.Name);
var dir = reader.GetFramesList (input);
if (dir != null)
{
int w = data.ToInt32 (frame_offset);
int h = data.ToInt32 (frame_offset+4);

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 Description { get { return Strings.arcStrings.XFLDescription; } }
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 ArcFile TryOpen (ArcView file)
{
uint dir_size = file.View.ReadUInt32 (4);
int count = file.View.ReadInt32 (8);
if (count <= 0)
var dir = ReadDirectory (file, 0, file.MaxOffset, "");
if (dir != null)
return new ArcFile (file, this, dir);
else
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;
file.View.Reserve (0, base_offset);
long cur_offset = 12;
file.View.Reserve (base_offset, (uint)(data_offset - base_offset));
long cur_offset = base_offset + 12;
var dir = new List<Entry> (count);
for (int i = 0; i < count; ++i)
{
if (cur_offset+40 > base_offset)
if (cur_offset+40 > data_offset)
return null;
string name = file.View.ReadString (cur_offset, 32);
var entry = FormatCatalog.Instance.Create<Entry> (name);
entry.Offset = base_offset + file.View.ReadUInt32 (cur_offset+32);
entry.Size = file.View.ReadUInt32 (cur_offset+36);
if (!entry.CheckPlacement (max_offset))
return null;
dir.Add (entry);
var entry_offset = data_offset + file.View.ReadUInt32 (cur_offset+32);
var entry_size = file.View.ReadUInt32 (cur_offset+36);
List<Entry> subdir = null;
name = VFS.CombinePath (base_dir, name);
if (name.HasExtension (".xfl") && file.View.ReadUInt32 (entry_offset) == Signature)
{
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;
}
return new ArcFile (file, this, dir);
return dir;
}
public override void Create (Stream output, IEnumerable<Entry> list, ResourceOptions options,

View File

@ -61,9 +61,9 @@ namespace GameRes.Formats.Maika
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[] {
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
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion ("1.2.48.2153")]
[assembly: AssemblyFileVersion ("1.2.48.2153")]
[assembly: AssemblyVersion ("1.2.48.2176")]
[assembly: AssemblyFileVersion ("1.2.48.2176")]

View File

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

View File

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

View File

@ -124,6 +124,7 @@ namespace GameRes.Formats.Seraphim
{
// common case for 256-colors images
Signatures = new uint[] { 0x01004243, 0 };
Extensions = new string[] { "CB", "CLB" };
}
public override ImageMetaData ReadMetaData (IBinaryStream stream)
@ -133,18 +134,18 @@ namespace GameRes.Formats.Seraphim
return null;
int colors = header.ToUInt16 (2);
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;
uint width = header.ToUInt16 (8);
uint height = header.ToUInt16 (10);
if (0 == width || 0 == height)
int width = header.ToInt16 (8);
int height = header.ToInt16 (10);
if (width <= 0 || height <= 0 || colors > 0x100)
return null;
return new SeraphMetaData
{
OffsetX = header.ToInt16 (4),
OffsetY = header.ToInt16 (6),
Width = width,
Height = height,
Width = (uint)width,
Height = (uint)height,
BPP = 8,
PackedSize = packed_size,
Colors = colors,
@ -363,7 +364,7 @@ namespace GameRes.Formats.Seraphim
private byte[] UnpackBytes () // sub_403ED0
{
int total = m_width * m_height;
var output = new byte[total];
var output = new byte[total + m_width];
int dst = 0;
while ( dst < total )
{
@ -450,9 +451,9 @@ namespace GameRes.Formats.Seraphim
}
else
{
int v36 = m_input.ReadByte() | ((next & 0xF) << 8);
int offset = m_input.ReadByte() | ((next & 0xF) << 8);
count = m_input.ReadByte() + 1;
int src = dst - 1 - v36;
int src = dst - 1 - offset;
Binary.CopyOverlapped (output, src, 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.
//
using System;
using System.ComponentModel.Composition;
using System.IO;
using System.Windows.Media;
@ -31,13 +32,21 @@ using GameRes.Utility;
namespace GameRes.Formats.TechnoBrain
{
internal class IpfMetaData : ImageMetaData
internal class IpfMetaData : ImageMetaData, ICloneable
{
public bool HasPalette;
public bool HasBitmap;
public bool IsCompressed;
public long PalOffset;
public int PalSize;
public long BmpOffset;
public long DataOffset;
public string FormatString;
public object Clone ()
{
return MemberwiseClone();
}
}
[Export(typeof(ImageFormat))]
@ -47,24 +56,25 @@ namespace GameRes.Formats.TechnoBrain
public override string Description { get { return "TechnoBrain's 'Inteligent Picture Format'"; } }
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.
if (0x46464952 != file.Signature) // 'RIFF'
return null;
var header = file.ReadHeader (0x14);
if (!header.AsciiEqual (8, "IPF fmt "))
if (!header.AsciiEqual (0xC, "fmt "))
return null;
int fmt_size = header.ToInt32 (0x10);
if (fmt_size < 0x24)
return null;
header = file.ReadHeader (0x14 + fmt_size);
bool has_palette = header.ToInt32 (0x18) != 0;
bool has_bitmap = header.ToInt32 (0x28) != 0;
if (!has_bitmap)
return null;
var info = new IpfMetaData { BPP = 8, HasPalette = has_palette };
if (has_palette)
var info = new IpfMetaData {
BPP = 8,
HasPalette = header.ToInt32 (0x18) != 0,
HasBitmap = header.ToInt32 (0x28) != 0,
FormatString = header.GetCString (8, 8),
};
if (info.HasPalette)
{
if (0x206C6170 != file.ReadInt32()) // 'pal '
return null;
@ -74,24 +84,43 @@ namespace GameRes.Formats.TechnoBrain
info.PalOffset = file.Position;
file.Position = info.PalOffset + info.PalSize;
}
info.DataOffset = file.Position;
return info;
}
internal bool ReadBmpInfo (IBinaryStream file, IpfMetaData info)
{
if (0x20706D62 != file.ReadInt32()) // 'bmp '
return null;
return false;
int bmp_size = file.ReadInt32();
if (bmp_size <= 0x20)
return null;
return false;
info.BmpOffset = file.Position + 0x18;
info.Width = 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);
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;
}
public override ImageData Read (IBinaryStream file, ImageMetaData info)
{
var reader = new IpfReader (file, (IpfMetaData)info);
var pixels = reader.Unpack();
return ImageData.Create (info, reader.Format, reader.Palette, pixels);
var reader = new IpfReader (file, (IpfMetaData)info, this);
return reader.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;
IpfMetaData m_info;
byte[] m_output;
ImageData m_image;
public BitmapPalette Palette { 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_info = info;
m_output = new byte[m_info.Width*m_info.Height];
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 ()
@ -151,7 +192,7 @@ namespace GameRes.Formats.TechnoBrain
for (int j = 0; j < 8; ++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)
{
@ -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)
{
uint header_size = Binary.BigEndian (file.View.ReadUInt32 (0));
uint file_size = Binary.BigEndian (file.View.ReadUInt32 (4));
if (file_size != file.MaxOffset || header_size > file_size || 0 == header_size)
return null;
long file_size = Binary.BigEndian (file.View.ReadUInt32 (4));
int format = Binary.BigEndian (file.View.ReadInt32 (8));
uint data_offset = Binary.BigEndian (file.View.ReadUInt32 (12));
if (format <= 0 || format > 0x100 || data_offset >= file_size || data_offset < header_size)
if (format <= 0 || format > 0x100)
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;
using (var stream = file.CreateStream())
using (var input = new AssetReader (stream))
@ -84,7 +91,7 @@ namespace GameRes.Formats.Unity
{
reader.SetupReaders (obj.Asset);
var tex = new Texture2D();
tex.Load (reader, obj.Asset.Tree.Version);
tex.Load (reader, obj.Asset.Tree);
if (0 == tex.m_DataLength)
{
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:
index_data = UnpackLzma (packed, index_size);
break;
case 2:
case 3:
index_data = new byte[index_size];
Lz4Compressor.DecompressBlock (packed, packed.Length, index_data, index_data.Length);

View File

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

View File

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

View File

@ -97,13 +97,13 @@ namespace GameRes.Formats.Unity
internal class StreamingInfo
{
public uint Offset;
public long Offset;
public uint Size;
public string Path;
public void Load (AssetReader reader)
{
Offset = reader.ReadUInt32();
Offset = reader.ReadOffset();
Size = reader.ReadUInt32();
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];
int packed_size = m_input.Read (m_packed, 0, (int)segment.PackedSize);
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);
else
throw new NotImplementedException ("Not supported Unity asset bundle compression.");

View File

@ -208,7 +208,7 @@ namespace GameRes.Formats.Vorbis
GranuleVals[LacingFill + i] = GranulePos;
}
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
LacingVals[LacingFill] |= 0x100;

View File

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

View File

@ -97,12 +97,17 @@ namespace GameRes.Formats.Unity
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);
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();
return;
}
@ -130,6 +135,35 @@ namespace GameRes.Formats.Unity
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)
{
m_Data = reader.ReadBytes (m_DataLength);
@ -243,6 +277,12 @@ namespace GameRes.Formats.Unity
pixels = ConvertArgb16 (m_texture.m_Data);
break;
case TextureFormat.BC7:
{
var decoder = new Bc7Decoder (m_texture.m_Data, Info);
pixels = decoder.Unpack();
break;
}
default:
throw new NotImplementedException (string.Format ("Not supported Unity Texture2D format '{0}'.", m_texture.m_TextureFormat));
}

View File

@ -226,7 +226,7 @@
</dependentAssembly>
<dependentAssembly>
<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>
</assemblyBinding>
</runtime>

View File

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

View File

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

View File

@ -54,6 +54,7 @@
</Reference>
<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>
<Private>True</Private>
</Reference>
<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>
@ -68,6 +69,8 @@
<Reference Include="System" />
<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>
<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 Include="System.ComponentModel.Composition" />
<Reference Include="System.Console, Version=4.0.1.1, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
@ -116,6 +119,23 @@
</Reference>
<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>
<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 Include="System.Web.Extensions" />
<Reference Include="System.Xaml" />
@ -128,6 +148,9 @@
<HintPath>..\packages\System.Xml.ReaderWriter.4.3.1\lib\net46\System.Xml.ReaderWriter.dll</HintPath>
</Reference>
<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>
<Compile Include="Artemis\ImageIPT.cs" />
@ -140,6 +163,7 @@
<Compile Include="RPGMaker\AudioRPGMV.cs" />
<Compile Include="RPGMaker\ImageRPGMV.cs" />
<Compile Include="Artemis\ShiftReduceParserCode.cs" />
<Compile Include="SakanaGL\ArcSX.cs" />
<Compile Include="WebP\ImageWEBP.cs" />
</ItemGroup>
<ItemGroup>
@ -188,6 +212,16 @@
</None>
<None Include="Artemis\IPT.parser" />
</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">
<Names>ipt</Names>
</PropertyGroup>
@ -204,6 +238,15 @@ exit 0</PreBuildEvent>
<PropertyGroup Label="GenerateIPTProperties">
<IPTParser>$(ProjectDir)Artemis\IPT</IPTParser>
</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="YltBuildGen" DependsOnTargets="GenerateIPT" />
<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\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="..\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')" />
<!-- 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.

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
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion ("1.0.16.39")]
[assembly: AssemblyFileVersion ("1.0.16.39")]
[assembly: AssemblyVersion ("1.0.16.40")]
[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>
<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>
</assemblyBinding>
</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"?>
<packages>
<package id="Concentus" version="1.1.7" targetFramework="net461" />
<package id="Concentus.Oggfile" version="1.0.4" targetFramework="net461" />
<package id="Microsoft.NETCore.Platforms" version="6.0.1" targetFramework="net461" />
<package id="Microsoft.Win32.Primitives" version="4.3.0" targetFramework="net461" />
<package id="MSFTCompressionCab" version="1.0.0" targetFramework="net461" />
<package id="NETStandard.Library" version="2.0.3" targetFramework="net461" />
<package id="Stub.System.Data.SQLite.Core.NetFramework" version="1.0.115.5" targetFramework="net461" />
<package id="System.AppContext" version="4.3.0" targetFramework="net461" />
<package id="System.Console" version="4.3.1" targetFramework="net461" />
<package id="System.Data.SQLite.Core" version="1.0.115.5" targetFramework="net461" />
<package id="System.Globalization.Calendars" version="4.3.0" targetFramework="net461" />
<package id="System.IO.Compression" version="4.3.0" targetFramework="net461" />
<package id="System.IO.Compression.ZipFile" version="4.3.0" targetFramework="net461" />
<package id="System.IO.FileSystem" version="4.3.0" targetFramework="net461" />
<package id="System.IO.FileSystem.Primitives" version="4.3.0" targetFramework="net461" />
<package id="System.Net.Http" version="4.3.4" targetFramework="net461" />
<package id="System.Net.Sockets" version="4.3.0" targetFramework="net461" />
<package id="System.Runtime.InteropServices.RuntimeInformation" version="4.3.0" targetFramework="net461" />
<package id="System.Security.Cryptography.Algorithms" version="4.3.1" targetFramework="net461" />
<package id="System.Security.Cryptography.Encoding" version="4.3.0" targetFramework="net461" />
<package id="System.Security.Cryptography.Primitives" version="4.3.0" targetFramework="net461" />
<package id="System.Security.Cryptography.X509Certificates" version="4.3.2" targetFramework="net461" />
<package id="System.Xml.ReaderWriter" version="4.3.1" targetFramework="net461" />
<package id="YaccLexTools" version="0.2.2" targetFramework="net461" />
<package id="Concentus" version="1.1.7" targetFramework="net46" />
<package id="Concentus.Oggfile" version="1.0.4" targetFramework="net46" />
<package id="Microsoft.NETCore.Platforms" version="6.0.3" targetFramework="net46" />
<package id="Microsoft.Win32.Primitives" version="4.3.0" targetFramework="net46" />
<package id="MSFTCompressionCab" version="1.0.0" targetFramework="net46" />
<package id="NETStandard.Library" version="2.0.3" targetFramework="net46" />
<package id="Stub.System.Data.SQLite.Core.NetFramework" version="1.0.115.5" targetFramework="net46" />
<package id="System.AppContext" version="4.3.0" targetFramework="net46" />
<package id="System.Buffers" version="4.5.1" targetFramework="net46" />
<package id="System.Console" version="4.3.1" targetFramework="net46" />
<package id="System.Data.SQLite.Core" version="1.0.115.5" targetFramework="net46" />
<package id="System.Globalization.Calendars" version="4.3.0" targetFramework="net46" />
<package id="System.IO.Compression" version="4.3.0" targetFramework="net46" />
<package id="System.IO.Compression.ZipFile" version="4.3.0" targetFramework="net46" />
<package id="System.IO.FileSystem" version="4.3.0" targetFramework="net46" />
<package id="System.IO.FileSystem.Primitives" version="4.3.0" targetFramework="net46" />
<package id="System.Memory" version="4.5.4" targetFramework="net46" />
<package id="System.Net.Http" version="4.3.4" targetFramework="net46" />
<package id="System.Net.Sockets" version="4.3.0" targetFramework="net46" />
<package id="System.Runtime.CompilerServices.Unsafe" version="4.6.0" targetFramework="net46" />
<package id="System.Runtime.InteropServices.RuntimeInformation" version="4.3.0" targetFramework="net46" />
<package id="System.Security.Cryptography.Algorithms" version="4.3.1" targetFramework="net46" />
<package id="System.Security.Cryptography.Encoding" version="4.3.0" targetFramework="net46" />
<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>

View File

@ -228,24 +228,39 @@ namespace GARbro.GUI
var src_format = ImageFormat.FindFormat (file);
if (null == src_format)
return;
if (src_format.Item1 == m_image_format && m_image_format.Extensions.Any (ext => ext == source_ext))
return;
file.Position = 0;
var image = src_format.Item1.Read (file, src_format.Item2);
var output = CreateNewFile (target_name);
Stream output = null;
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
{
// FIXME if user chooses to overwrite file, and conversion results in error,
// then original file will be lost.
output.Dispose();
output = null;
File.Delete (target_name);
throw;
}
output.Dispose();
finally
{
if (output != null)
output.Dispose();
}
}
}
@ -264,5 +279,12 @@ namespace GARbro.GUI
m_main.ListViewFocus();
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))
{
SetPreviewImage (preview, data.Image.Bitmap);
SetPreviewImage (preview, data.Image.Bitmap, data.SourceFormat);
}
}
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)
{
@ -271,7 +271,7 @@ namespace GARbro.GUI
ImageCanvas.Source = bitmap;
ApplyDownScaleSetting();
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
{
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();
}
catch
finally
{
ResumeWatchDirectoryChanges();
throw;
}
RefreshView();
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
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion ("1.5.44.2904")]
[assembly: AssemblyFileVersion ("1.5.44.2904")]
[assembly: AssemblyVersion ("1.5.44.2938")]
[assembly: AssemblyFileVersion ("1.5.44.2938")]

View File

@ -119,42 +119,86 @@ namespace GARbro.Shell
/// <summary>
/// SHFILEOPSTRUCT for SHFileOperation from COM
/// </summary>
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto, Pack = 1)]
private struct SHFILEOPSTRUCT
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode, Pack = 1)]
private struct SHFILEOPSTRUCT32
{
public IntPtr hwnd;
[MarshalAs(UnmanagedType.U4)]
public FileOperationType wFunc;
[MarshalAs(UnmanagedType.LPTStr)]
public string pFrom;
[MarshalAs(UnmanagedType.LPTStr)]
public string pTo;
public FileOperationFlags fFlags;
[MarshalAs(UnmanagedType.Bool)]
public bool fAnyOperationsAborted;
public IntPtr hNameMappings;
[MarshalAs(UnmanagedType.LPTStr)]
public string lpszProgressTitle;
}
[DllImport("shell32.dll", CharSet = CharSet.Auto)]
private static extern int SHFileOperation (ref SHFILEOPSTRUCT FileOp);
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
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>
/// Send file to recycle bin
/// </summary>
/// <param name="path">Location of directory or file to recycle</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
{
wFunc = FileOperationType.FO_DELETE,
pFrom = path + '\0' + '\0',
fFlags = FileOperationFlags.FOF_ALLOWUNDO | flags
};
return 0 == SHFileOperation (ref fs);
return 0 == SHFileOperation (FileOperationType.FO_DELETE, path+'\0'+'\0',
FileOperationFlags.FOF_ALLOWUNDO | flags, parent);
}
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();
foreach (var file in file_list)
@ -165,36 +209,31 @@ namespace GARbro.Shell
if (0 == files.Length)
return false;
files.Append ('\0');
var fs = new SHFILEOPSTRUCT
{
wFunc = FileOperationType.FO_DELETE,
pFrom = files.ToString(),
fFlags = FileOperationFlags.FOF_ALLOWUNDO | flags
};
return 0 == SHFileOperation (ref fs);
return 0 == SHFileOperation (FileOperationType.FO_DELETE, files.ToString(),
FileOperationFlags.FOF_ALLOWUNDO | flags, parent);
}
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>
/// Send file to recycle bin. Display dialog, display warning if files are too big to fit (FOF_WANTNUKEWARNING)
/// </summary>
/// <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>
/// Send file silently to recycle bin. Surpress dialog, surpress errors, delete if too large.
/// </summary>
/// <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>
</data>
<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>
</data>
<data name="MsgNoFiles" xml:space="preserve">

View File

@ -232,7 +232,7 @@
<value>{0}에서 {1}로 파일 추출하기</value>
</data>
<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 name="MsgNoFiles" xml:space="preserve">
<value>추출할 파일이 없음</value>

View File

@ -232,7 +232,7 @@
<value>Extracting files from {0} to {1}</value>
</data>
<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 name="MsgNoFiles" xml:space="preserve">
<value>no files to extract</value>

View File

@ -226,7 +226,7 @@
<value>Извлекаются файлы из {0} в {1}</value>
</data>
<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 name="MsgNoFiles" xml:space="preserve">
<value>отсутствуют файлы, удовлетворяющие выбранным критериям</value>

View File

@ -232,7 +232,7 @@
<value>正在从{0}中提取文件至{1}……</value>
</data>
<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 name="MsgNoFiles" xml:space="preserve">
<value>没有可以提取的文件。</value>

View File

@ -1,7 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="NAudio" version="1.10.0" targetFramework="net461" />
<package id="WindowsAPICodePack-Core" version="1.1.2" targetFramework="net461" />
<package id="WindowsAPICodePack-Shell" version="1.1.1" targetFramework="net461" />
<package id="WPFToolkit" version="3.5.50211.1" targetFramework="net461" />
<package id="NAudio" version="1.10.0" targetFramework="net46" />
<package id="WindowsAPICodePack-Core" version="1.1.2" targetFramework="net46" />
<package id="WindowsAPICodePack-Shell" version="1.1.1" targetFramework="net46" />
<package id="WPFToolkit" version="3.5.50211.1" targetFramework="net46" />
</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
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion ("1.5.44.319")]
[assembly: AssemblyFileVersion ("1.5.44.319")]
[assembly: AssemblyVersion ("1.5.44.320")]
[assembly: AssemblyFileVersion ("1.5.44.320")]

View File

@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="NAudio" version="1.10.0" targetFramework="net461" />
<package id="NAudio" version="1.10.0" targetFramework="net46" />
</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 uint Signature { get { return 0x6F6F746B; } } // 'ktool210'
public CgdFormat ()
{
Signatures = new uint[] { 0x6F6F746B, 0x65697073 };
}
public override ImageMetaData ReadMetaData (IBinaryStream file)
{
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;
uint offset = header.ToUInt32 (0x10) & 0x7FFFFFFF;
return CgdMetaData.FromStream (file, offset);

View File

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

View File

@ -27,6 +27,7 @@ using System;
using System.ComponentModel.Composition;
using System.IO;
using System.Windows;
using System.Windows.Media;
using System.Windows.Media.Imaging;
namespace GameRes.Formats.Pochette
@ -93,15 +94,21 @@ namespace GameRes.Formats.Pochette
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))
return overlay;
{
base_name += ".gdt";
if (!VFS.FileExists (base_name))
return overlay;
}
using (var base_file = VFS.OpenBinaryStream (base_name))
{
var base_info = ReadMetaData (base_file) as GdtMetaData;
if (null == base_info)
return overlay;
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);
int canvas_bpp = canvas.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
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion ("1.0.10.196")]
[assembly: AssemblyFileVersion ("1.0.10.196")]
[assembly: AssemblyVersion ("1.0.10.198")]
[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>
<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>
</assemblyBinding>
</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;
sub get_git_exe {
my $user_app_data = Win32::GetFolderPath (Win32::CSIDL_LOCAL_APPDATA);
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;
return "git.exe";
}
sub match_version {