diff --git a/GARbro.sln b/GARbro.sln index 108a5b04..fec1f4ff 100644 --- a/GARbro.sln +++ b/GARbro.sln @@ -30,6 +30,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SchemeBuilder", "SchemeBuil EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Experimental", "Experimental\Experimental.csproj", "{60054FD9-4472-4BB4-9E3D-2F80D3D22468}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Legacy", "Legacy\Legacy.csproj", "{C79E82A8-8D32-485D-8442-2D4F71FBB5D5}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -69,11 +71,12 @@ Global {60054FD9-4472-4BB4-9E3D-2F80D3D22468}.Prerelease|Any CPU.ActiveCfg = Prerelease|Any CPU {60054FD9-4472-4BB4-9E3D-2F80D3D22468}.Release|Any CPU.ActiveCfg = Release|Any CPU {60054FD9-4472-4BB4-9E3D-2F80D3D22468}.Release|Any CPU.Build.0 = Release|Any CPU + {C79E82A8-8D32-485D-8442-2D4F71FBB5D5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {C79E82A8-8D32-485D-8442-2D4F71FBB5D5}.Debug|Any CPU.Build.0 = Debug|Any CPU + {C79E82A8-8D32-485D-8442-2D4F71FBB5D5}.Prerelease|Any CPU.ActiveCfg = Prerelease|Any CPU + {C79E82A8-8D32-485D-8442-2D4F71FBB5D5}.Release|Any CPU.ActiveCfg = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection - GlobalSection(Performance) = preSolution - HasPerformanceSessions = true - EndGlobalSection EndGlobal diff --git a/Legacy/ApplePie/ArcARC.cs b/Legacy/ApplePie/ArcARC.cs new file mode 100644 index 00000000..e5502ad7 --- /dev/null +++ b/Legacy/ApplePie/ArcARC.cs @@ -0,0 +1,69 @@ +//! \file ArcARC.cs +//! \date 2017 Dec 05 +//! \brief ApplePie resource archive. +// +// Copyright (C) 2017 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.ApplePie +{ + [Export(typeof(ArchiveFormat))] + public class ArcOpener : ArchiveFormat + { + public override string Tag { get { return "ARC/ApplePie"; } } + public override string Description { get { return "Apple Pie resource archive"; } } + public override uint Signature { get { return 0x10435241; } } // 'ARC\x10' + public override bool IsHierarchic { get { return false; } } + public override bool CanWrite { get { return false; } } + + public override ArcFile TryOpen (ArcView file) + { + int count = file.View.ReadInt32 (4); + if (!IsSaneCount (count)) + return null; + uint index_offset = file.View.ReadUInt32 (0xC); + uint index_size = (uint)count * 24; + if (index_offset >= file.MaxOffset + || index_size > file.View.Reserve (index_offset, index_size)) + return null; + var dir = new List (count); + for (int i = 0; i < count; ++i) + { + var name = file.View.ReadString (index_offset, 0x10); + if (string.IsNullOrEmpty (name)) + return null; + var entry = FormatCatalog.Instance.Create (name); + entry.Size = file.View.ReadUInt32 (index_offset+0x10); + entry.Offset = file.View.ReadUInt32 (index_offset+0x14); + if (!entry.CheckPlacement (file.MaxOffset)) + return null; + dir.Add (entry); + index_offset += 0x18; + } + return new ArcFile (file, this, dir); + } + } +} diff --git a/Legacy/ApplePie/ImageGT.cs b/Legacy/ApplePie/ImageGT.cs new file mode 100644 index 00000000..de3c5dd1 --- /dev/null +++ b/Legacy/ApplePie/ImageGT.cs @@ -0,0 +1,111 @@ +//! \file ImageGT.cs +//! \date 2017 Dec 05 +//! \brief ApplePie image format. +// +// Copyright (C) 2017 by morkt +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +// + +using System; +using System.ComponentModel.Composition; +using System.IO; +using System.Windows.Media; + +namespace GameRes.Formats.ApplePie +{ + internal class GtMetaData : ImageMetaData + { + public int Flags; + public uint DataOffset; + } + + [Export(typeof(ImageFormat))] + public class GtFormat : ImageFormat + { + public override string Tag { get { return "GT/ApplePie"; } } + public override string Description { get { return "Apple Pie image format"; } } + public override uint Signature { get { return 0; } } + + public GtFormat () + { + Signatures = new uint[] { 0x0B105447, 0x06105447, 0x01105447, 0 }; + } + + public override ImageMetaData ReadMetaData (IBinaryStream file) + { + var header = file.ReadHeader (0x10); + if (!header.AsciiEqual ("GT\x10")) + return null; + bool has_alpha = (header[3] & 8) != 0; + bool grayscale = (header[3] & 3) == 1; + return new GtMetaData { + Width = header.ToUInt16 (4), + Height = header.ToUInt16 (6), + BPP = grayscale ? 8 : has_alpha ? 32 : 24, + Flags = header[3], + DataOffset = header.ToUInt32 (8), + }; + } + + public override ImageData Read (IBinaryStream file, ImageMetaData info) + { + var meta = (GtMetaData)info; + file.Position = meta.DataOffset; + if (0 != (meta.Flags & 8)) + { + var pixels = UnpackRle (file, (int)meta.Width * (int)meta.Height); + return ImageData.Create (info, PixelFormats.Bgra32, null, pixels); + } + else if (1 == (meta.Flags & 3)) + { + var pixels = file.ReadBytes ((int)meta.Width * (int)meta.Height); + return ImageData.Create (info, PixelFormats.Gray8, null, pixels); + } + else + { + int stride = (int)meta.Width * 3; + var pixels = file.ReadBytes (stride * (int)meta.Height); + return ImageData.Create (info, PixelFormats.Bgr24, null, pixels, stride); + } + } + + byte[] UnpackRle (IBinaryStream input, int count) + { + var output = new byte[count * 4]; + int dst = 0; + while (count > 0) + { + int length = input.ReadInt32(); + count -= length; + dst += length * 4; + length = input.ReadInt32(); + count -= length; + input.Read (output, dst, length * 4); + dst += length * 4; + } + return output; + } + + public override void Write (Stream file, ImageData image) + { + throw new System.NotImplementedException ("GtFormat.Write not implemented"); + } + } +} diff --git a/Legacy/Clio/ArcPAC.cs b/Legacy/Clio/ArcPAC.cs new file mode 100644 index 00000000..a0e923a2 --- /dev/null +++ b/Legacy/Clio/ArcPAC.cs @@ -0,0 +1,67 @@ +//! \file ArcPAC.cs +//! \date 2017 Dec 03 +//! \brief Clio resource archive. +// +// Copyright (C) 2017 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.Clio +{ + [Export(typeof(ArchiveFormat))] + public class PacOpener : ArchiveFormat + { + public override string Tag { get { return "PAC/CLIO"; } } + public override string Description { get { return "Clio 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) + { + int count = file.View.ReadInt32 (0); + if (!file.Name.HasExtension (".pac") || !IsSaneCount (count)) + return null; + + uint index_offset = 4; + uint data_offset = index_offset + (uint)count * 0x28u; + var dir = new List (count); + for (int i = 0; i < count; ++i) + { + var name = file.View.ReadString (index_offset, 0x20); + if (string.IsNullOrWhiteSpace (name)) + return null; + var entry = FormatCatalog.Instance.Create (name); + entry.Size = file.View.ReadUInt32 (index_offset+0x20); + entry.Offset = file.View.ReadUInt32 (index_offset+0x24); + if (entry.Offset < data_offset || !entry.CheckPlacement (file.MaxOffset)) + return null; + dir.Add (entry); + index_offset += 0x28; + } + return new ArcFile (file, this, dir); + } + } +} diff --git a/Legacy/Clio/ImageEXP.cs b/Legacy/Clio/ImageEXP.cs new file mode 100644 index 00000000..f1e562b6 --- /dev/null +++ b/Legacy/Clio/ImageEXP.cs @@ -0,0 +1,161 @@ +//! \file ImageEXP.cs +//! \date 2017 Dec 04 +//! \brief Clio compressed bitmap format. +// +// Copyright (C) 2017 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; + +namespace GameRes.Formats.Clio +{ + internal class ExpMetaData : ImageMetaData + { + public int BitmapSize; + public byte[] BitmapFileName; + } + + [Export(typeof(ImageFormat))] + public class ExpFormat : ImageFormat + { + public override string Tag { get { return "EXP/CLIO"; } } + public override string Description { get { return "Clio compressed bitmap"; } } + public override uint Signature { get { return 0x4E455850; } } // 'PXEN' + + public override ImageMetaData ReadMetaData (IBinaryStream file) + { + file.Position = 4; + var filename = file.ReadBytes (0x20); + int bitmap_size = file.ReadInt32(); + var reader = new ExpReader (file, filename); + var bmp_header = reader.Unpack (0x36); + using (var mem_bmp = new BinMemoryStream (bmp_header, file.Name)) + { + var info = Bmp.ReadMetaData (mem_bmp); + if (null == info) + return null; + return new ExpMetaData { + Width = info.Width, + Height = info.Height, + BPP = info.BPP, + BitmapSize = bitmap_size, + BitmapFileName = filename, + }; + } + } + + public override ImageData Read (IBinaryStream file, ImageMetaData info) + { + var meta = (ExpMetaData)info; + file.Position = 0x28; + var reader = new ExpReader (file, meta.BitmapFileName); + var bmp_data = reader.Unpack (meta.BitmapSize); + using (var mem_bmp = new BinMemoryStream (bmp_data, file.Name)) + return Bmp.Read (mem_bmp, info); + } + + public override void Write (Stream file, ImageData image) + { + throw new System.NotImplementedException ("ExpFormat.Write not implemented"); + } + } + + internal class ExpReader + { + IBinaryStream m_input; + byte[] m_filename; + + public ExpReader (IBinaryStream input, byte[] filename) + { + m_input = input; + m_filename = filename.Clone() as byte[]; + } + + public byte[] Unpack (int unpacked_size) + { + var output = new byte[unpacked_size]; + int dst = 0; + var table = new byte[2,256]; + while (dst < unpacked_size && m_input.PeekByte() != -1) + { + for (int i = 0; i < 256; ++i) + table[0,i] = (byte)i; + int count; + int t_idx = 0; + do + { + byte ctl = m_input.ReadUInt8(); + if (ctl > 127) + { + t_idx += ctl - 127; + ctl = 0; + } + if (t_idx != 256) + { + count = ctl + 1; + while (count --> 0) + { + ctl = (byte)t_idx; + table[0,t_idx] = m_input.ReadUInt8(); + if (t_idx != table[0,t_idx]) + { + table[1,t_idx] = m_input.ReadUInt8(); + } + ++t_idx; + } + } + } + while (t_idx != 256); + byte hi = m_input.ReadUInt8(); + byte lo = m_input.ReadUInt8(); + count = hi << 8 | lo; + int pos = 0; + for (;;) + { + byte b; + if (pos != 0) + { + b = m_filename[--pos]; + } + else + { + if (0 == count--) + break; + b = m_input.ReadUInt8(); + } + if (b == table[0,b]) + { + output[dst++] = b; + if (dst >= output.Length) + break; + } + else + { + m_filename[pos++] = table[1,b]; + m_filename[pos++] = table[0,b]; + } + } + } + return output; + } + } +} diff --git a/Legacy/Factor/ArcRES.cs b/Legacy/Factor/ArcRES.cs new file mode 100644 index 00000000..3a80584f --- /dev/null +++ b/Legacy/Factor/ArcRES.cs @@ -0,0 +1,130 @@ +//! \file ArcRES.cs +//! \date 2017 Nov 29 +//! \brief Factor resource archive. +// +// Copyright (C) 2017 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.Text.RegularExpressions; + +namespace GameRes.Formats.Factor +{ + [Export(typeof(ArchiveFormat))] + public class PackOpener : ArchiveFormat + { + public override string Tag { get { return "PACK/FACTOR"; } } + public override string Description { get { return "Factor resource archive"; } } + public override uint Signature { get { return 0; } } + public override bool IsHierarchic { get { return false; } } + public override bool CanWrite { get { return false; } } + + public PackOpener () + { + Extensions = new string[] { "" }; + } + + static readonly Regex PackNameRe = new Regex (@"^pack(\d)$"); + + public override ArcFile TryOpen (ArcView file) + { + var base_name = Path.GetFileNameWithoutExtension (file.Name); + var match = PackNameRe.Match (base_name); + if (!match.Success) + return null; + var pack_ext = Path.GetExtension (file.Name); + List names = new List(); + /* + if (pack_ext.Equals (".bmp", StringComparison.InvariantCultureIgnoreCase)) + { + var res_name = Path.ChangeExtension (file.Name, "res"); + var res_num = match.Groups[1].Value; + names.AddRange (ReadNames (res_name, res_num)); + } + */ + var dir = new List(); + long offset = 0; + int i = 0; + while (offset < file.MaxOffset) + { + uint size = file.View.ReadUInt32 (offset); + offset += 4; + string name; + if (i < names.Count) + name = names[i]; + else + name = string.Format ("{0}#{1:D4}{2}", base_name, i, pack_ext); + var entry = FormatCatalog.Instance.Create (name); + entry.Offset = offset; + entry.Size = size; + if (!entry.CheckPlacement (file.MaxOffset)) + return null; + dir.Add (entry); + ++i; + offset += size; + } + return new ArcFile (file, this, dir); + } + + public override Stream OpenEntry (ArcFile arc, Entry entry) + { + Stream input = arc.File.CreateStream (entry.Offset, entry.Size); + if (entry.Name.HasExtension (".res")) + input = new XoredStream (input, 0x80); + return input; + } + + static readonly Regex FirstLineRe = new Regex (@"\\(\d)\\$"); + + IEnumerable ReadNames (string res_name, string num) + { + if (!VFS.FileExists (res_name)) + yield break; + using (var pack = VFS.OpenView (res_name)) + { + uint offset = 4 + pack.View.ReadUInt32 (0); + offset += 4 + pack.View.ReadUInt32 (offset); + uint size = pack.View.ReadUInt32 (offset); + offset += 4; + if (offset >= pack.MaxOffset) + yield break; + using (var res = pack.CreateStream (offset, size)) + using (var decrypted = new XoredStream (res, 0x80)) + using (var input = new StreamReader (decrypted, Encodings.cp932)) + { + var line = input.ReadLine(); + if (string.IsNullOrEmpty (line)) + yield break; + var match = FirstLineRe.Match (line); + if (!match.Success || match.Groups[1].Value != num) + yield break; + while ((line = input.ReadLine()) != null) + { + yield return line; + } + } + } + } + } +} diff --git a/Legacy/Legacy.csproj b/Legacy/Legacy.csproj new file mode 100644 index 00000000..a2854fab --- /dev/null +++ b/Legacy/Legacy.csproj @@ -0,0 +1,89 @@ + + + + + Debug + AnyCPU + {C79E82A8-8D32-485D-8442-2D4F71FBB5D5} + Library + Properties + GameRes.Legacy + ArcLegacy + v4.5 + 512 + + + true + full + false + ..\bin\Debug\ + DEBUG;TRACE + prompt + 4 + true + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + bin\Prerelease\ + TRACE + true + pdbonly + AnyCPU + prompt + MinimumRecommendedRules.ruleset + + + + + + + + + + + + + + + + + + + + + + + + + + + + {a8865685-27cc-427b-ac38-e48d2ad05df4} + ArcFormats + + + {453c087f-e416-4ae9-8c03-d8760da0574b} + GameRes + + + + + + perl "$(SolutionDir)inc-revision.pl" "$(ProjectPath)" $(ConfigurationName) +exit 0 + + + \ No newline at end of file diff --git a/Legacy/Nekotaro/ArcNSC.cs b/Legacy/Nekotaro/ArcNSC.cs new file mode 100644 index 00000000..51fc02a0 --- /dev/null +++ b/Legacy/Nekotaro/ArcNSC.cs @@ -0,0 +1,71 @@ +//! \file ArcNSC.cs +//! \date 2017 Dec 01 +//! \brief Nekotaro Game System resource archive. +// +// Copyright (C) 2017 by morkt +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +// + +using System.Collections.Generic; +using System.ComponentModel.Composition; +using System.IO; + +namespace GameRes.Formats.Nekotaro +{ + [Export(typeof(ArchiveFormat))] + public class NscOpener : ArchiveFormat + { + public override string Tag { get { return "NSC"; } } + public override string Description { get { return "Nekotaro Game System resource archive"; } } + public override uint Signature { get { return 0x4643534E; } } // 'NSCF' + public override bool IsHierarchic { get { return false; } } + public override bool CanWrite { get { return false; } } + + public override ArcFile TryOpen (ArcView file) + { + var base_name = Path.GetFileNameWithoutExtension (file.Name); + var dir = new List(); + uint prev_offset = file.View.ReadUInt32 (4); + for (uint index_offset = 8; index_offset < file.MaxOffset; index_offset += 4) + { + uint offset = file.View.ReadUInt32 (index_offset); + if (offset <= prev_offset || offset > file.MaxOffset) + return null; + uint size = offset - prev_offset; + var name = string.Format ("{0}#{1:D4}", base_name, dir.Count); + Entry entry; + if (size > 4) + entry = AutoEntry.Create (file, prev_offset, name); + else + entry = new Entry { Name = name, Offset = prev_offset }; + entry.Size = size; + if (!entry.CheckPlacement (file.MaxOffset)) + return null; + dir.Add (entry); + if (file.MaxOffset == offset) + break; + prev_offset = offset; + } + if (0 == dir.Count) + return null; + return new ArcFile (file, this, dir); + } + } +} diff --git a/Legacy/Nekotaro/ImageGCmp.cs b/Legacy/Nekotaro/ImageGCmp.cs new file mode 100644 index 00000000..d9d5cb94 --- /dev/null +++ b/Legacy/Nekotaro/ImageGCmp.cs @@ -0,0 +1,275 @@ +//! \file ImageGCmp.cs +//! \date 2017 Dec 01 +//! \brief Nekotaro Game System compressed image format. +// +// Copyright (C) 2017 by morkt +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +// + +using System; +using System.ComponentModel.Composition; +using System.IO; +using System.Windows.Media; +using System.Windows.Media.Imaging; +using GameRes.Utility; + +namespace GameRes.Formats.Nekotaro +{ + [Export(typeof(ImageFormat))] + public class GCmpFormat : ImageFormat + { + public override string Tag { get { return "GCMP"; } } + public override string Description { get { return "Nekotaro Game System image format"; } } + public override uint Signature { get { return 0x706D4347; } } // 'GCmp' + + public override ImageMetaData ReadMetaData (IBinaryStream file) + { + var header = file.ReadHeader (0x10); + int bpp = header[12]; + if (bpp != 24 && bpp != 8 && bpp != 1) + return null; + return new ImageMetaData { + Width = header.ToUInt16 (8), + Height = header.ToUInt16 (10), + BPP = bpp, + }; + } + + public override ImageData Read (IBinaryStream file, ImageMetaData info) + { + using (var reader = new GCmpDecoder (file, info, this, true)) + return reader.Image; + } + + public override void Write (Stream file, ImageData image) + { + throw new System.NotImplementedException ("GCmpFormat.Write not implemented"); + } + } + + internal sealed class GCmpDecoder : IImageDecoder + { + IBinaryStream m_input; + ImageData m_image; + bool m_should_dispose; + + public Stream Source { get { return m_input.AsStream; } } + public ImageFormat SourceFormat { get; private set; } + public ImageMetaData Info { get; private set; } + public PixelFormat Format { get; private set; } + public BitmapPalette Palette { get; private set; } + public int Stride { get; private set; } + public ImageData Image { + get { + if (null == m_image) + { + var pixels = Unpack(); + m_image = ImageData.CreateFlipped (Info, Format, Palette, pixels, Stride); + } + return m_image; + } + } + + public GCmpDecoder (IBinaryStream input, ImageMetaData info, ImageFormat source, bool leave_open = false) + { + m_input = input; + Info = info; + SourceFormat = source; + m_should_dispose = !leave_open; + if (info.BPP > 1) + Stride = (int)info.Width * info.BPP / 8; + else + Stride = ((int)info.Width + 7) / 8; + } + + public byte[] Unpack () + { + m_input.Position = 0x10; + if (24 == Info.BPP) + return Unpack24bpp(); + else + return Unpack8bpp(); + } + + byte[] Unpack24bpp () + { + Format = PixelFormats.Bgr24; + int pixel_count = (int)(Info.Width * Info.Height); + var output = new byte[pixel_count * Info.BPP / 8 + 1]; + var frame = new byte[384]; + int dst = 0; + int v19 = 0; + while (pixel_count > 0) + { + int count, frame_pos, pixel; + if (v19 != 0) + { + pixel = m_input.ReadInt24(); + count = 1; + frame_pos = 127; + --v19; + } + else + { + count = m_input.ReadUInt8(); + int lo = count & 0x1F; + if (0 != (count & 0x80)) + { + count = ((byte)count >> 5) & 3; + if (count != 0) + { + frame_pos = lo; + } + else + { + count = lo << 1; + frame_pos = m_input.ReadUInt8(); + if (0 != (frame_pos & 0x80)) + ++count; + frame_pos &= 0x7F; + } + if (0 == count) + { + count = m_input.ReadInt32(); + } + int fpos = 3 * frame_pos; + pixel = frame[fpos] | frame[fpos+1] << 8 | frame[fpos+2] << 16; + } + else + { + if (1 == count) + { + v19 = m_input.ReadUInt8() - 1; + } + else if (0 == count) + { + count = m_input.ReadInt32(); + } + pixel = m_input.ReadInt24(); + frame_pos = 127; + } + } + if (count > pixel_count) + count = pixel_count; + pixel_count -= count; + LittleEndian.Pack (pixel, output, dst); + dst += 3; + if (--count > 0) + { + count *= 3; + Binary.CopyOverlapped (output, dst - 3, dst, count); + dst += count; + } + if (frame_pos != 0) + Buffer.BlockCopy (frame, 0, frame, 3, 3 * frame_pos); + frame[0] = (byte)pixel; + frame[1] = (byte)(pixel >> 8); + frame[2] = (byte)(pixel >> 16); + } + return output; + } + + byte[] Unpack8bpp () + { + Format = 8 == Info.BPP ? PixelFormats.Gray8 : PixelFormats.BlackWhite; + int pixel_count = (int)Info.Height * Stride; + var output = new byte[pixel_count]; + int dst = 0; + byte[] frame = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 0xFF }; + + int count = pixel_count; + int extra_count = pixel_count; + while (pixel_count > 0) + { + byte pixel; + int frame_pos; + byte ctl = m_input.ReadUInt8(); + int hi = ctl >> 4; + int lo = ctl & 0xF; + if (hi != 0) + { + frame_pos = hi - 1; + pixel = frame[frame_pos]; + count = lo + 1; + } + else + { + switch (lo) + { + default: + count = lo + 1; + break; + case 10: + count = m_input.ReadUInt8() + 11; + break; + case 11: + count = m_input.ReadUInt16() + 267; + break; + case 12: + count = m_input.ReadInt32() + 65803; + break; + case 13: + extra_count = 0x10; + count = m_input.ReadUInt8(); + break; + case 14: + extra_count = 0x120; + count = m_input.ReadUInt16(); + break; + case 15: + extra_count = 0x10130; + count = m_input.ReadInt32(); + break; + } + pixel = m_input.ReadUInt8(); + if (lo < 13) + { + frame_pos = 14; + } + else + { + lo = pixel & 0xF; + frame_pos = (pixel >> 4) - 1; + pixel = frame[frame_pos]; + count = extra_count + 16 * count + lo + 1; + } + } + if (count > pixel_count) + count = pixel_count; + pixel_count -= count; + for (int i = 0; i < count; ++i) + output[dst++] = pixel; + Buffer.BlockCopy (frame, 0, frame, 1, frame_pos); + frame[0] = pixel; + } + return output; + } + + bool m_disposed = false; + public void Dispose () + { + if (!m_disposed && m_should_dispose) + { + m_input.Dispose(); + m_disposed = true; + } + } + } +} diff --git a/Legacy/Properties/AssemblyInfo.cs b/Legacy/Properties/AssemblyInfo.cs new file mode 100644 index 00000000..7717862b --- /dev/null +++ b/Legacy/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("Legacy")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("Legacy")] +[assembly: AssemblyCopyright("Copyright © 2017")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("db136566-69ed-4c86-a10d-e52979696ae0")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// 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.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/Legacy/README.md b/Legacy/README.md new file mode 100644 index 00000000..4b4a1611 --- /dev/null +++ b/Legacy/README.md @@ -0,0 +1,4 @@ +Legacy File Formats +=================== + +Collection of formats that were used in visual novels developed in late 90s/early 2000s. diff --git a/Legacy/Rain/ArcBIN.cs b/Legacy/Rain/ArcBIN.cs new file mode 100644 index 00000000..3dcc4dd5 --- /dev/null +++ b/Legacy/Rain/ArcBIN.cs @@ -0,0 +1,92 @@ +//! \file ArcBIN.cs +//! \date 2017 Dec 03 +//! \brief Rain Software resource archive. +// +// Copyright (C) 2017 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.Text.RegularExpressions; +using GameRes.Compression; + +namespace GameRes.Formats.Rain +{ + [Export(typeof(ArchiveFormat))] + public class BinOpener : ArchiveFormat + { + public override string Tag { get { return "BIN/RAIN"; } } + public override string Description { get { return "Rain Software resource archive"; } } + public override uint Signature { get { return 0; } } + public override bool IsHierarchic { get { return false; } } + public override bool CanWrite { get { return false; } } + + static readonly Regex PackNameRe = new Regex (@"^pack(...)\.bin$", RegexOptions.IgnoreCase); + + public override ArcFile TryOpen (ArcView file) + { + int count = file.View.ReadInt32 (0); + bool is_compressed = 0 == (count & 0x80000000); + count &= 0x7FFFFFFF; + if (!IsSaneCount (count)) + return null; + var match = PackNameRe.Match (Path.GetFileName (file.Name)); + if (!match.Success) + return null; + var ext = match.Groups[1].Value; + uint index_size = (uint)count * 12; + if (index_size > file.View.Reserve (4, index_size)) + return null; + uint index_offset = 4; + uint data_offset = 4 + index_size; + var dir = new List (count); + var seen_nums = new HashSet(); + for (int i = 0; i < count; ++i) + { + uint num = file.View.ReadUInt32 (index_offset); + if (num > 0xFFFFFF || !seen_nums.Add (num)) + return null; + var name = string.Format ("{0:D5}.{1}", num, ext); + var entry = FormatCatalog.Instance.Create (name); + entry.Offset = file.View.ReadUInt32 (index_offset+4); + entry.Size = file.View.ReadUInt32 (index_offset+8); + if (entry.Offset < data_offset || !entry.CheckPlacement (file.MaxOffset)) + return null; + dir.Add (entry); + index_offset += 12; + } + return new ArcFile (file, this, dir); + } + + public override Stream OpenEntry (ArcFile arc, Entry entry) + { + if (!arc.File.View.AsciiEqual (entry.Offset, "SZDD")) + return base.OpenEntry (arc, entry); + var input = arc.File.CreateStream (entry.Offset+12, entry.Size-12); + var lzss = new LzssStream (input); + lzss.Config.FrameFill = 0x20; + lzss.Config.FrameInitPos = 0xFF0; + return lzss; + } + } +} diff --git a/Legacy/ShapeShifter/ArcBND.cs b/Legacy/ShapeShifter/ArcBND.cs new file mode 100644 index 00000000..62f13310 --- /dev/null +++ b/Legacy/ShapeShifter/ArcBND.cs @@ -0,0 +1,92 @@ +//! \file ArcBND.cs +//! \date 2017 Nov 29 +//! \brief BND resource archive. +// +// Copyright (C) 2017 by morkt +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +// + +using System; +using System.Collections.Generic; +using System.ComponentModel.Composition; +using System.IO; +using GameRes.Utility; + +namespace GameRes.Formats.ShapeShifter +{ + [Export(typeof(ArchiveFormat))] + public class BndOpener : ArchiveFormat + { + public override string Tag { get { return "BND"; } } + public override string Description { get { return "Shape Shifter 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) + { + int count = file.View.ReadInt32 (0); + if (!IsSaneCount (count)) + return null; + + uint first_offset = file.View.ReadUInt32 (4); + if (first_offset != 4 +(uint)count * 12) + return null; + + var base_name = Path.GetFileNameWithoutExtension (file.Name); + string default_type = base_name == "SCR" ? "script" : ""; + uint index_offset = 4; + var dir = new List (count); + for (int i = 0; i < count; ++i) + { + uint offset = file.View.ReadUInt32 (index_offset); + var entry = new PackedEntry { + Name = string.Format ("{0}#{1:D4}", base_name, i), + Type = default_type, + Offset = file.View.ReadUInt32 (index_offset), + UnpackedSize = file.View.ReadUInt32 (index_offset+4), + Size = file.View.ReadUInt32 (index_offset+8), + }; + if (!entry.CheckPlacement (file.MaxOffset)) + return null; + entry.IsPacked = entry.UnpackedSize != entry.Size; + dir.Add (entry); + index_offset += 12; + } + if (string.IsNullOrEmpty (default_type)) + DetectFileTypes (file, dir); + return new ArcFile (file, this, dir); + } + + void DetectFileTypes (ArcView file, List dir) + { + foreach (var entry in dir) + { + var offset = entry.Offset; + var signature = file.View.ReadUInt32 (offset); + if (0x4D42 == (signature & 0xFFFF)) // 'BM' + { + entry.Type = "image"; + entry.Name = Path.ChangeExtension (entry.Name, "bmp"); + } + } + } + } +}