From 718ceb4c4048487b40b4c8751ef3713aef3efdd8 Mon Sep 17 00:00:00 2001 From: ManicSteiner Date: Tue, 19 Dec 2023 15:52:13 +0800 Subject: [PATCH 1/6] feat: ArcGPDA of PCSG00543 Saenai Heroine no Sodatekata Blessing Flowers --- ArcFormats/ArcFormats.csproj | 1 + ArcFormats/MAGES/ArcGPDA.cs | 49 ++++++++++++++++++++++++++++++++++++ 2 files changed, 50 insertions(+) create mode 100644 ArcFormats/MAGES/ArcGPDA.cs diff --git a/ArcFormats/ArcFormats.csproj b/ArcFormats/ArcFormats.csproj index 0057045d..df8b82df 100644 --- a/ArcFormats/ArcFormats.csproj +++ b/ArcFormats/ArcFormats.csproj @@ -175,6 +175,7 @@ + diff --git a/ArcFormats/MAGES/ArcGPDA.cs b/ArcFormats/MAGES/ArcGPDA.cs new file mode 100644 index 00000000..b2ee7b7f --- /dev/null +++ b/ArcFormats/MAGES/ArcGPDA.cs @@ -0,0 +1,49 @@ +using System.Collections.Generic; +using System.ComponentModel.Composition; +using System.Text; + +namespace GameRes.Formats.MAGES +{ + [Export(typeof(ArchiveFormat))] + public class GpdaOpener : ArchiveFormat + { + public override string Tag { get { return "DAT/GPDA"; } } + public override string Description { get { return "PCSG00543 resource archive"; } } + public override uint Signature { get { return 0x41445047; } } // 'GPDA' + 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(0x0C); + if (!IsSaneCount(count)) + return null; + var dir = new List(count); + for (int i = 0; i < count; ++i) + { + uint filename_offset = file.View.ReadUInt32(16 * i + 16 + 12); + uint filename_length = file.View.ReadUInt32(filename_offset) - 1; + string name = file.View.ReadString(filename_offset + 4, filename_length); + /*byte c; + List namebyte = new List(); + while (true) + { + c = file.View.ReadByte((long)index_offset); + if (c == 0 | index_offset > filename_end) break; + namebyte.Add(c); + index_offset++; + }*/ + //var sjis = System.Text.Encoding.GetEncoding("Shift-JIS"); + //var name = Encoding.ASCII.GetString(namebyte.ToArray()); + var entry = Create(name); + + entry.Offset = file.View.ReadUInt32(16 * i + 16); + entry.Size = file.View.ReadUInt32(16 * i + 16 + 8); + if (!entry.CheckPlacement(file.MaxOffset)) + return null; + dir.Add(entry); + } + return new ArcFile(file, this, dir); + } + } +} From 53d8fdd844de9ab127adbacf3cd2c18a62daebe0 Mon Sep 17 00:00:00 2001 From: ManicSteiner Date: Thu, 21 Dec 2023 13:50:20 +0800 Subject: [PATCH 2/6] feat: PS2 BIP format --- ArcFormats/ArcFormats.csproj | 2 + ArcFormats/Kid/ImageBIP.cs | 164 ++++++++++++++++++++++++++++++++++ ArcFormats/Kid/ImageBIParc.cs | 44 +++++++++ 3 files changed, 210 insertions(+) create mode 100644 ArcFormats/Kid/ImageBIP.cs create mode 100644 ArcFormats/Kid/ImageBIParc.cs diff --git a/ArcFormats/ArcFormats.csproj b/ArcFormats/ArcFormats.csproj index df8b82df..e4c4bf52 100644 --- a/ArcFormats/ArcFormats.csproj +++ b/ArcFormats/ArcFormats.csproj @@ -132,6 +132,8 @@ + + diff --git a/ArcFormats/Kid/ImageBIP.cs b/ArcFormats/Kid/ImageBIP.cs new file mode 100644 index 00000000..6f641cf2 --- /dev/null +++ b/ArcFormats/Kid/ImageBIP.cs @@ -0,0 +1,164 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel.Composition; +using System.IO; +using System.Text; +using System.Windows; +using System.Windows.Media; +using System.Windows.Media.Media3D; + +namespace GameRes.Formats.Kid +{ + public class BipFormat : ImageFormat + { + public override string Tag { get { return "BIP/PS2"; } } + public override string Description { get { return "PS2 tiled bitmap format"; } } + public override uint Signature { get { return 0; } } //0x05000000 + + public override ImageMetaData ReadMetaData(IBinaryStream file) + { + uint header = file.ReadUInt32(); + if (header == 9) + { + throw new NotSupportedException(string.Format("BIP Chara format not supported.")); + } + else if (header != 5) + { + return null; + } + + file.Seek(0x14, SeekOrigin.Begin); + if (file.ReadUInt16() != 256) + { + return null; + } + uint sign = file.ReadUInt16(); + uint dy = 0; + bool sliced = true; + if (sign == 0x17) + { + dy = 16; + } + else if (sign == 0x16) + { + dy = 16; + sliced = false; + } + else if (sign == 0x13) + { + dy = 32; + } + else + { + return null; + } + + //uint dx = dy * 32; + file.Seek(0x88, SeekOrigin.Begin); + uint width = file.ReadUInt16(); + uint height = file.ReadUInt16(); + if (width >= 1280 || height >= 1280) // suppose not so large + return null; + + file.Seek(0x90, SeekOrigin.Begin); + uint size = file.ReadUInt32(); + size &= 0x00FFFFFF; + if (sign == 0x13 && size != (file.Length - 0x100) || sign == 0x17 && size != (file.Length - 0x100)* 2) + { + return null; + } + return new BipImageMetaData + { + Width = width, + Height = height, + BlockSize = dy, + Sliced = sliced, + }; + } + public override ImageData Read(IBinaryStream file, ImageMetaData info) + { + if (info == null) + throw new NotSupportedException(string.Format("Not BIP texture format.")); + var bipheader = (BipImageMetaData)info; + file.Seek(0x100, SeekOrigin.Begin); + byte[] pixels = new byte[bipheader.iWidth * bipheader.iHeight * 4]; + uint dy = bipheader.BlockSize; + uint dx = dy * 32; + if (bipheader.Sliced) + { + long dwidth = ((bipheader.iWidth + (dy - 2) - 1) / (dy - 2)) * dy; + long dheight = ((bipheader.iHeight + (dy - 2) - 1) / (dy - 2)) * dy; + long focus_H = (dwidth* dheight + dx - 1) / dx; + long focus_T = (focus_H + dy - 1) / dy; + + for (int t = 0; t < focus_T; t++) + { + for(int y = 0; y < dy; y++) + { + for (int x = 0; x < dx; x++) + { + var pixel = file.ReadBytes(4); //RGBA with wrong A + long i2x = x + t * dx; + long i3t = i2x / dwidth; + long i3x = i2x - i3t * dwidth; + long i3y = i3t * (dy - 2) + y; + long i4x = i3x - i3x / dy * dy + i3x / dy * (dy - 2); + if (i3x >= dwidth || i4x >= bipheader.iWidth || i3y >= bipheader.iHeight) + continue; + long target = (i4x + i3y * bipheader.iWidth) * 4; + //BGRA + pixels[target] = pixel[2]; + pixels[target + 1] = pixel[1]; + pixels[target + 2] = pixel[0]; + if (pixel[3] >= byte.MaxValue / 2) + pixels[target + 3] = byte.MaxValue; + else + pixels[target + 3] = (byte)(pixel[3] << 1); + } + } + } + } + else + { + long focus_H = (bipheader.iWidth * bipheader.iHeight + dx - 1) / dx; + long focus_T = (focus_H + dy - 1) / dy; + for (int t = 0; t < focus_T; t++) + { + for (int y = 0; y < dy; y++) + { + for (int x = 0; x < dx; x++) + { + var pixel = file.ReadBytes(4); //RGBA with wrong A + long i2x = x + t * dx; + long i3t = i2x / bipheader.iWidth; + long i3x = i2x - i3t * bipheader.iWidth; + long i3y = i3t * dy + y; + if (i3x >= bipheader.iWidth || i3y >= bipheader.iHeight) + continue; + long target = (i3x + i3y * bipheader.iWidth) * 4; + //BGRA + pixels[target] = pixel[2]; + pixels[target + 1] = pixel[1]; + pixels[target + 2] = pixel[0]; + if (pixel[3] >= byte.MaxValue / 2) + pixels[target + 3] = byte.MaxValue; + else + pixels[target + 3] = (byte)(pixel[3] << 1); + } + } + } + } + return ImageData.Create(info, PixelFormats.Bgra32, null, pixels); ; + } + public override void Write(Stream file, ImageData image) + { + throw new System.NotImplementedException("BipFormat.Write not implemented"); + } + + class BipImageMetaData : ImageMetaData + { + public uint BlockSize { get; set; } + public bool Sliced { get; set; } + } + } +} diff --git a/ArcFormats/Kid/ImageBIParc.cs b/ArcFormats/Kid/ImageBIParc.cs new file mode 100644 index 00000000..8a7cafa4 --- /dev/null +++ b/ArcFormats/Kid/ImageBIParc.cs @@ -0,0 +1,44 @@ +using GameRes.Compression; +using System; +using System.Collections.Generic; +using System.ComponentModel.Composition; +using System.IO; + +namespace GameRes.Formats.Kid +{ + [Export(typeof(ImageFormat))] + public class BipArkFormat: BipFormat + { + public override string Tag { get { return "BIP/PS2 compressed"; } } + public override string Description { get { return "PS2 tiled bitmap format with lzss compress"; } } + public override uint Signature { get { return 0; } } + public BipArkFormat() + { + Extensions = new string[] { "bip" }; + } + + public override ImageMetaData ReadMetaData(IBinaryStream stream) + { + uint unpacked_size = stream.Signature; + if (unpacked_size <= 0x20 || unpacked_size > 0x5000000) // ~83MB + return null; + stream.Position = 4; + using (var lzss = new LzssStream(stream.AsStream, LzssMode.Decompress, true)) + using (var input = new SeekableStream(lzss)) + using (var bip = new BinaryStream(input, stream.Name)) + return base.ReadMetaData(bip); + } + public override ImageData Read(IBinaryStream stream, ImageMetaData info) + { + stream.Position = 4; + using (var lzss = new LzssStream(stream.AsStream, LzssMode.Decompress, true)) + using (var input = new SeekableStream(lzss)) + using (var bip = new BinaryStream(input, stream.Name)) + return base.Read(bip, info); + } + public override void Write(Stream file, ImageData image) + { + throw new System.NotImplementedException("BipFormat.Write not implemented"); + } + } +} From eb60e27378c79b662e8db359681ca97e7bfb56a9 Mon Sep 17 00:00:00 2001 From: ManicSteiner Date: Sat, 23 Dec 2023 23:30:09 +0800 Subject: [PATCH 3/6] perf: Various fixes --- ArcFormats/Ivory/ImageMOE.cs | 3 +++ ArcFormats/Kid/ArcDATRAW.cs | 2 ++ ArcFormats/Kid/ImageBIP.cs | 10 +++++----- 3 files changed, 10 insertions(+), 5 deletions(-) diff --git a/ArcFormats/Ivory/ImageMOE.cs b/ArcFormats/Ivory/ImageMOE.cs index ac976e8f..5c73e231 100644 --- a/ArcFormats/Ivory/ImageMOE.cs +++ b/ArcFormats/Ivory/ImageMOE.cs @@ -103,6 +103,9 @@ namespace GameRes.Formats.Ivory bool IsValidInput (Stream input, uint width, uint height, int pixel_size) { int total = (int)width * (int)height; + // Other formats will be incorrectly recognized as this format, try to correct it. + if (total * pixel_size < input.Length / 10) + return false; int dst = 0; while (dst < total) { diff --git a/ArcFormats/Kid/ArcDATRAW.cs b/ArcFormats/Kid/ArcDATRAW.cs index 3ce1f311..9928ab5d 100644 --- a/ArcFormats/Kid/ArcDATRAW.cs +++ b/ArcFormats/Kid/ArcDATRAW.cs @@ -38,6 +38,8 @@ namespace GameRes.Formats.Kid entry.Size = size; dir.Add(entry); } + if (dir.Count == 0) + return null; return new ArcFile(file, this, dir); } diff --git a/ArcFormats/Kid/ImageBIP.cs b/ArcFormats/Kid/ImageBIP.cs index 6f641cf2..7e8eb9b3 100644 --- a/ArcFormats/Kid/ImageBIP.cs +++ b/ArcFormats/Kid/ImageBIP.cs @@ -20,7 +20,7 @@ namespace GameRes.Formats.Kid uint header = file.ReadUInt32(); if (header == 9) { - throw new NotSupportedException(string.Format("BIP Chara format not supported.")); + return null;//throw new NotSupportedException(string.Format("BIP Chara format not supported.")); } else if (header != 5) { @@ -33,18 +33,18 @@ namespace GameRes.Formats.Kid return null; } uint sign = file.ReadUInt16(); - uint dy = 0; + uint dy; bool sliced = true; if (sign == 0x17) { dy = 16; } - else if (sign == 0x16) + else if (sign == 0x13) { dy = 16; sliced = false; } - else if (sign == 0x13) + else if (sign == 0x16) { dy = 32; } @@ -57,7 +57,7 @@ namespace GameRes.Formats.Kid file.Seek(0x88, SeekOrigin.Begin); uint width = file.ReadUInt16(); uint height = file.ReadUInt16(); - if (width >= 1280 || height >= 1280) // suppose not so large + if (width >= 1280 || height >= 1280 || width == 0 || height == 0) // suppose not so large return null; file.Seek(0x90, SeekOrigin.Begin); From 200a05db81833f6969db421674a7817f027f66fd Mon Sep 17 00:00:00 2001 From: ManicSteiner Date: Sun, 24 Dec 2023 00:50:16 +0800 Subject: [PATCH 4/6] fix: PS2 BIP Compress --- ArcFormats/Cri/ArcSPC.cs | 4 ++-- ArcFormats/Kid/ImageBIP.cs | 20 +++++++++++++------- 2 files changed, 15 insertions(+), 9 deletions(-) diff --git a/ArcFormats/Cri/ArcSPC.cs b/ArcFormats/Cri/ArcSPC.cs index 91272a99..1185d556 100644 --- a/ArcFormats/Cri/ArcSPC.cs +++ b/ArcFormats/Cri/ArcSPC.cs @@ -42,12 +42,12 @@ namespace GameRes.Formats.Cri public SpcOpener () { - Extensions = new string[] { "spc" }; + Extensions = new string[] { "spc", "bip" }; } public override ArcFile TryOpen (ArcView file) { - if (!file.Name.HasExtension (".spc")) + if (!file.Name.HasExtension (".spc") && !file.Name.HasExtension(".bip")) return null; uint unpacked_size = file.View.ReadUInt32 (0); if (unpacked_size <= 0x20 || unpacked_size > 0x5000000) diff --git a/ArcFormats/Kid/ImageBIP.cs b/ArcFormats/Kid/ImageBIP.cs index 7e8eb9b3..d3883512 100644 --- a/ArcFormats/Kid/ImageBIP.cs +++ b/ArcFormats/Kid/ImageBIP.cs @@ -28,24 +28,27 @@ namespace GameRes.Formats.Kid } file.Seek(0x14, SeekOrigin.Begin); - if (file.ReadUInt16() != 256) + if ((file.ReadUInt16() & 0x7FFF) != 256) { return null; } uint sign = file.ReadUInt16(); uint dy; bool sliced = true; - if (sign == 0x17) + if (sign == 0x17 || sign == 0x19 || sign == 0x1F || sign == 0x25 || sign == 0x2E || sign == 0x34) { + //sign = focusT / 2 dy = 16; } else if (sign == 0x13) { + //sign = focusT / 2 dy = 16; sliced = false; } - else if (sign == 0x16) + else if (sign == 0x16 || sign == 0x20 || sign == 0x2A) { + //sign = focusT * 2 dy = 32; } else @@ -57,13 +60,16 @@ namespace GameRes.Formats.Kid file.Seek(0x88, SeekOrigin.Begin); uint width = file.ReadUInt16(); uint height = file.ReadUInt16(); - if (width >= 1280 || height >= 1280 || width == 0 || height == 0) // suppose not so large + if (width > 2560 || height > 1440 || width < 16 || height < 16) // suppose not so large return null; file.Seek(0x90, SeekOrigin.Begin); - uint size = file.ReadUInt32(); - size &= 0x00FFFFFF; - if (sign == 0x13 && size != (file.Length - 0x100) || sign == 0x17 && size != (file.Length - 0x100)* 2) + //uint size = file.ReadUInt32(); // not size + uint sizesign = file.ReadUInt16(); + uint sizesign_high = file.ReadUInt16(); + //size &= 0x00FFFFFF; + //if (sign == 0x13 && size != (file.Length - 0x100) || sign == 0x17 && size != (file.Length - 0x100)* 2) + if (sizesign != 0 || sizesign_high == 0) { return null; } From 583249c955e7544f398139806935e7594e2d8542 Mon Sep 17 00:00:00 2001 From: ManicSteiner Date: Sun, 25 Aug 2024 23:32:21 +0800 Subject: [PATCH 5/6] feat: SPC-PS2 nearly complete support --- ArcFormats/Kid/ImageBIP.cs | 102 +++++++++++++++++++++++++------------ 1 file changed, 69 insertions(+), 33 deletions(-) diff --git a/ArcFormats/Kid/ImageBIP.cs b/ArcFormats/Kid/ImageBIP.cs index d3883512..c49e5fd6 100644 --- a/ArcFormats/Kid/ImageBIP.cs +++ b/ArcFormats/Kid/ImageBIP.cs @@ -18,50 +18,52 @@ namespace GameRes.Formats.Kid public override ImageMetaData ReadMetaData(IBinaryStream file) { uint header = file.ReadUInt32(); - if (header == 9) - { - return null;//throw new NotSupportedException(string.Format("BIP Chara format not supported.")); - } - else if (header != 5) + uint real_sign; + bool multi = false; // not implemented + if (header < 5) { return null; } + else if (header == 5) + { + real_sign = 0x14; + } + else if (header >= 6 && header <= 0x0C) + { + real_sign = header * 4; + multi = true; + } + else + { + return null; // throw new NotSupportedException(string.Format("BIP Chara format not supported.")); + } - file.Seek(0x14, SeekOrigin.Begin); - if ((file.ReadUInt16() & 0x7FFF) != 256) + file.Seek(0x10, SeekOrigin.Begin); + uint fstart = file.ReadUInt16(); // usually 0x100 + + // Non-Sequential read + file.Seek(0x88, SeekOrigin.Begin); // next one +20 + uint width = file.ReadUInt16(); + uint height = file.ReadUInt16(); + if (width > 2560 || height > 1440 || width < 16 || height < 16) // suppose not so large or so small + return null; + + file.Seek(real_sign, SeekOrigin.Begin); // usually 0x14 + if ((file.ReadUInt16() & 0x7FFF) != fstart) { return null; } uint sign = file.ReadUInt16(); uint dy; bool sliced = true; - if (sign == 0x17 || sign == 0x19 || sign == 0x1F || sign == 0x25 || sign == 0x2E || sign == 0x34) + long denominator = file.Length; + if (multi) { - //sign = focusT / 2 - dy = 16; + file.Seek(0xA2, SeekOrigin.Begin); + uint f2start = file.ReadUInt16(); + denominator = f2start * 1024 + fstart + 0x100; } - else if (sign == 0x13) - { - //sign = focusT / 2 - dy = 16; - sliced = false; - } - else if (sign == 0x16 || sign == 0x20 || sign == 0x2A) - { - //sign = focusT * 2 - dy = 32; - } - else - { - return null; - } - - //uint dx = dy * 32; - file.Seek(0x88, SeekOrigin.Begin); - uint width = file.ReadUInt16(); - uint height = file.ReadUInt16(); - if (width > 2560 || height > 1440 || width < 16 || height < 16) // suppose not so large - return null; + double oversize = (double)width * height * 4 / denominator; file.Seek(0x90, SeekOrigin.Begin); //uint size = file.ReadUInt32(); // not size @@ -73,12 +75,43 @@ namespace GameRes.Formats.Kid { return null; } + uint pixelsize = (sizesign_high & 0x00FF) * 0x10000; /*r16: 2*pixelsize r32: pixelsize*/ + + if (oversize > 0.87 /*sign == 0x13*/) + { + // no-sliced oversize min: 0.87178 max: 0.9866 + // sliced oversize max: 0.8333 min: 0.76 + dy = 16; + sliced = false; + // sign = focusT / 2 + } + else if (sign > 0x50) + { + return null; + } + else if (((double)pixelsize / denominator) <= 1.1 /*known oversize 1.02 for <=1, suppose fail > 1.8*/) + { + //sign % 2 == 0 && sign <= 0x2A && sign != 0x22 && sign != 0x1A + //sign == 0x16 || sign == 0x20 || sign == 0x2A + dy = 32; + // sign = focusT * 2 + } + else + { + //sign == 0x17 || sign == 0x19 || sign == 0x1F || sign == 0x25 || sign == 0x2E || sign == 0x34 + dy = 16; + // sign = focusT / 2 + } + + // uint dx = dy * 32; + return new BipImageMetaData { Width = width, Height = height, BlockSize = dy, Sliced = sliced, + Multi = multi, }; } public override ImageData Read(IBinaryStream file, ImageMetaData info) @@ -86,7 +119,9 @@ namespace GameRes.Formats.Kid if (info == null) throw new NotSupportedException(string.Format("Not BIP texture format.")); var bipheader = (BipImageMetaData)info; - file.Seek(0x100, SeekOrigin.Begin); + file.Seek(0x10, SeekOrigin.Begin); + uint fstart = file.ReadUInt16(); // usually 0x100 + file.Seek(fstart, SeekOrigin.Begin); byte[] pixels = new byte[bipheader.iWidth * bipheader.iHeight * 4]; uint dy = bipheader.BlockSize; uint dx = dy * 32; @@ -165,6 +200,7 @@ namespace GameRes.Formats.Kid { public uint BlockSize { get; set; } public bool Sliced { get; set; } + public bool Multi { get; set; } } } } From 52878652ef26e203528c848ccf9c72638b8af124 Mon Sep 17 00:00:00 2001 From: ManicSteiner Date: Mon, 26 Aug 2024 11:55:58 +0800 Subject: [PATCH 6/6] fix: MOAR BIP --- ArcFormats/Kid/ImageBIP.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/ArcFormats/Kid/ImageBIP.cs b/ArcFormats/Kid/ImageBIP.cs index c49e5fd6..a3f01570 100644 --- a/ArcFormats/Kid/ImageBIP.cs +++ b/ArcFormats/Kid/ImageBIP.cs @@ -89,8 +89,9 @@ namespace GameRes.Formats.Kid { return null; } - else if (((double)pixelsize / denominator) <= 1.1 /*known oversize 1.02 for <=1, suppose fail > 1.8*/) + else if (((double)pixelsize / denominator) <= 1.045 && sign != 0x2E && sign != 0x33) { + /*known oversize 1.02 for expected <=1, noticed smallest r16 oversize 1.045+ for MOAR2 EV_SP04A, 0.99+ for MOAR1 OM2_TT13A, 0.89+ for MOAR3 C3_30A*/ //sign % 2 == 0 && sign <= 0x2A && sign != 0x22 && sign != 0x1A //sign == 0x16 || sign == 0x20 || sign == 0x2A dy = 32;