Compare commits

...

108 Commits

Author SHA1 Message Date
Crsky
f63834c4bf Merge pull request #66 from mos9527/master
Add Support "The Song Of Saya (Steam)"
2024-12-23 16:17:36 +08:00
Crsky
8f96a1bccd Merge pull request #67 from Manicsteiner/feat_pacf
add support of F Fanatic PC version archive
2024-12-22 09:47:52 +08:00
ManicSteiner
f9def97321 add support of F Fanatic PC version archive 2024-12-21 22:49:01 +08:00
mos9527
301b795de6 Add Support "The Song Of Saya (Steam)" 2024-12-21 11:25:19 +08:00
Crsky
cd8fcea992 Merge pull request #65 from YeLikesss/master
[kirikiri] 夢幻のティル・ナ・ノーグ
2024-12-20 17:21:01 +08:00
YeLike
ddbeddd862 [kirikiri] 夢幻のティル・ナ・ノーグ 2024-12-20 16:04:12 +08:00
Crsky
82d44aeae2 Merge pull request #62 from YeLikesss/master
[Musica] Legacy Support(Wind a breath of heart Series 2002 Ver.)
2024-12-12 19:28:27 +08:00
YeLike
a7021686e9 [Musica] Legacy Game Support 2024-12-12 16:17:54 +08:00
Crsky
cdd4e0161c Merge pull request #61 from Manicsteiner/feat_pacps2
Add support Digital Works PS2 PAC
2024-12-09 13:58:56 +08:00
ManicSteiner
8424d75eab Add support Digital Works PS2 PAC 2024-12-09 10:58:11 +08:00
Crsky
9cb5ec89b3 Merge pull request #59 from YeLikesss/master
[Musica] Fix SqzFile Parser
2024-12-03 19:28:27 +08:00
YeLike
ac063d7eb5 [Musica] Fix SqzFile Parser 2024-12-03 18:19:05 +08:00
Crsky
7210ff5ff3 Merge pull request #53 from Dir-A/master
(Valkyria): support "MICOCG02"
2024-10-01 13:09:55 +08:00
Dir-A
ccc7c8032b (Valkyria): support "MICOCG02"
tested game: https://vndb.org/v48766
2024-10-01 06:49:25 +08:00
Crsky
499ac02c64 Add support "Hinadori no Ochiru Oto" 2024-09-24 15:48:37 +08:00
Crsky
a20aa9434c Merge pull request #50 from Dir-A/master
Update ArcISA.cs
2024-09-24 15:44:44 +08:00
Dir-A
b967d505ec Update ImageSeraph.cs
fix image format check
2024-09-24 04:17:30 +08:00
Dir-A
4554b82be5 Update ArcISA.cs
Add support "Sisters: Last Day of Summer" English Version
https://vndb.org/r109826
2024-09-24 01:58:42 +08:00
Crsky
e642644845 Fix line breaks. 2024-09-15 04:01:02 +08:00
Crsky
4a16bb3e9e Update BinaryStream.cs 2024-09-15 02:32:24 +08:00
Crsky
6eae25b574 Merge pull request #48 from YeLikesss/master
[Nexas]UTF-8 & Zstd Support
2024-09-15 02:27:29 +08:00
Crsky
a8d73d24bb Update ArcFormats.csproj 2024-09-15 02:16:47 +08:00
Crsky
ab7b799c7d Update ArcPAC.cs 2024-09-15 02:13:23 +08:00
Crsky
ba065f76af Update ArcPAC.cs 2024-09-15 02:11:39 +08:00
Crsky
afd4d5f7fa Update packages.config 2024-09-15 01:51:37 +08:00
Crsky
d76abea03c Update Settings.Designer.cs 2024-09-15 01:49:16 +08:00
YeLike
08e5a28058 [Nexas]UTF-8 & Zstd Support 2024-09-14 20:57:14 +08:00
Crsky
d4f7fa2eed Add support "Shoujo☆Kageki" 2024-09-12 20:53:08 +08:00
Crsky
5915546fdf Merge pull request #46 from MkfsSion/ageengine
ArcFormats: Support S5IC archieve
2024-09-12 06:48:50 +08:00
Crsky
3120d9477b Merge pull request #45 from MkfsSion/yanefix
ArcFormats: Fix TryOpen() for YaneSDK .dat archive
2024-09-07 09:57:32 +08:00
Crsky
c00f8621da Merge pull request #43 from Manicsteiner/feat_bip
Feat BIP-PS2 support
2024-09-07 02:01:57 +08:00
MkfsSion
ca196d8286 ArcFormats: Support split ALF APPEND archive
* Tested game: https://vndb.org/r89951

Signed-off-by: MkfsSion <[email protected]>
2024-09-06 23:18:11 +08:00
MkfsSion
7bd244e228 ArcFormats: Support S3AC archieve
* Tested game: https://vndb.org/r5111

Signed-off-by: MkfsSion <[email protected]>
2024-09-06 23:15:51 +08:00
MkfsSion
d621cf4c03 ArcFormats: Support S5IC archieve
* Tested game: https://vndb.org/r119690

Signed-off-by: MkfsSion <[email protected]>
2024-09-06 23:12:58 +08:00
Crsky
7b3bb7e730 Merge pull request #39 from SlawekNowy/master
Add support for DXArchive8.
2024-09-06 10:48:17 +08:00
MkfsSion
092b544c87 ArcFormats: Fix TryOpen() for YaneSDK .dat archive
* Tested game: https://vndb.org/r11278

Signed-off-by: MkfsSion <[email protected]>
2024-09-04 23:52:49 +08:00
ManicSteiner
52878652ef fix: MOAR BIP 2024-08-26 11:55:58 +08:00
ManicSteiner
583249c955 feat: SPC-PS2 nearly complete support 2024-08-25 23:32:21 +08:00
Manicsteiner
2d930b2191 Merge branch 'crskycode:master' into feat_bip 2024-08-24 12:06:07 +08:00
Sławomir Śpiewak
ad7f4f1788 Simplify code. 2024-08-20 15:01:08 +02:00
Sławomir Śpiewak
329589122a Fix cases where files with non-ascii names would fail to decrypt. 2024-08-18 10:46:41 +02:00
Sławomir Śpiewak
d2a22fa2fb Update ArcDX8.cs
We already read the entry to the array. Don't attempt to read twice. Or in other words, just copy from the prepared array.
2024-08-14 18:30:45 +02:00
Sławomir Śpiewak
0817dacd99 Update ArcDX8.cs
More fixes.
This is not done yet.
2024-08-11 18:59:30 +02:00
Sławomir Śpiewak
ed0f0489d3 Update HuffmanDecoder.cs
Fix not so obvious errors.
2024-08-11 15:41:21 +02:00
Sławomir Śpiewak
57749e5cac Update HuffmanDecoder.cs
Fix obvious bugs.
2024-08-07 17:51:43 +02:00
Sławomir Śpiewak
4773ff148f Update HuffmanDecoder.cs 2024-08-05 16:14:04 +02:00
Sławomir Śpiewak
2ad5095cec Update HuffmanDecoder.cs
compressedSize does not include header.
2024-08-05 16:07:31 +02:00
Sławomir Śpiewak
3f4c263ec5 Update HuffmanDecoder.cs
Since Huffman node is now a class it must be explicitely initiated.
2024-08-05 15:47:28 +02:00
Sławomir Śpiewak
4d5be92386 Update HuffmanDecoder.cs
Fix constructor visibility error;
Change to big endian 32 bit.
2024-08-05 15:44:55 +02:00
Sławomir Śpiewak
e3236c0fbd Revert "Add ToUint64 for big endian numbers"
This reverts commit 681e82dd3f.
2024-08-05 15:43:28 +02:00
Sławomir Śpiewak
681e82dd3f Add ToUint64 for big endian numbers 2024-08-05 15:37:02 +02:00
Sławomir Śpiewak
071a33eb94 Actually init arrays. 2024-08-05 15:17:26 +02:00
Sławomir Śpiewak
ab67cb5187 Finish up implementation.
Debugging next.
2024-08-05 15:07:56 +02:00
Sławomir Śpiewak
aa2900f15b Implement construction of DxLib's Huffman tree 2024-08-02 19:29:19 +02:00
Sławomir Śpiewak
881e5f1e3f Update ArcDX8.cs
Start work on huffman compression.
2024-08-01 14:42:40 +02:00
Sławomir Śpiewak
beaab0ecee Fixes 2024-08-01 11:53:08 +02:00
Sławomir Śpiewak
45e7af60ea Fix issues. 2024-08-01 11:46:41 +02:00
Sławomir Śpiewak
738b2950f6 Fix logic error. 2024-07-31 16:32:53 +02:00
Sławomir Śpiewak
6a90018798 Apply suggestions from upstream 2024-07-31 16:32:35 +02:00
Sławomir Śpiewak
3bd697577c Multiple Changes:
DXA8 now directly asks for password, as guessing it, is made improbable.
Promote several private methods to `protected` status
Add code for indexing DXA8 files. (not finished)
2024-07-17 19:53:13 +02:00
Sławomir Śpiewak
30e04eae1f Update ArcDX8.cs 2024-07-15 14:22:34 +02:00
Sławomir Śpiewak
b3f5195a9a Ensure that DXA is not "decrypted" when not needed. 2024-07-15 14:20:17 +02:00
Sławomir Śpiewak
2e8057aa54 Update ArcDX8.cs
Add anything that can be done without user input.
2024-07-15 14:15:14 +02:00
Sławomir Śpiewak
ddabe6d2f4 Fix broken ASCII pointer. 2024-07-15 14:14:52 +02:00
Sławomir Śpiewak
2a6f3ab70c Add support for DXArchive8 encryption. 2024-07-14 20:59:18 +02:00
Crsky
e2d4ddb59a Add support "Hanagane Kanade * Gram - Chapter:3 Hoshiizumi Kotona Download Edition" 2024-07-09 03:36:51 +08:00
Crsky
8fcb85334e (Kirikiri): Add Kirikiri Z archive creating support. 2024-05-26 15:30:45 +08:00
Crsky
7f74a030a2 Add support "Gakuen Tokkyuu Hotokenser" 2024-04-23 21:36:27 +08:00
Crsky
682a5fd2e7 Merge pull request #26 from Manicsteiner/feat_ci
Feat ci build script
2024-02-23 18:34:47 +08:00
ManicSteiner
13f2923dc4 remove SchemeBuilder 2024-02-23 11:42:47 +08:00
ManicSteiner
92b809e95b add ci build script 2024-02-23 11:38:01 +08:00
ManicSteiner
c1008aba3e Merge branch 'master' of https://github.com/Manicsteiner/GARbro 2024-02-23 11:22:02 +08:00
Crsky
b68b11a1a2 Add support "Ego's Spark - 18+ DLC" 2024-02-22 21:01:14 +08:00
Crsky
4124b369de Remove 'Auto delete pdb' 2024-02-09 00:03:10 +08:00
Crsky
92c54b72ae Add support "Dies irae Interview with Kaziklu Bey [ENG]" 2024-02-08 23:02:03 +08:00
Crsky
3c01e689a6 Add support "Silverio Trinity -Beyond the Horizon-" 2024-02-08 23:01:24 +08:00
Crsky
e69ce728ec Add support "Silverio Vendetta -Verse of Orpheus-" 2024-02-08 23:00:35 +08:00
Crsky
54ca6d8f1f Add support "Silverio Ragnarok" 2024-02-08 22:59:33 +08:00
Crsky
64a88dc045 Merge pull request #19 from FredericaBernkastel/kg_encoder
(Candysoft/KG) encoder implementation
2023-12-31 00:56:28 +08:00
Frederica Bernkastel
741f50619e optimize 2023-12-30 04:01:40 +06:00
Frederica Bernkastel
824ea431de KG encoder 2023-12-29 22:55:57 +06:00
ManicSteiner
200a05db81 fix: PS2 BIP Compress 2023-12-24 00:50:16 +08:00
ManicSteiner
eb60e27378 perf: Various fixes 2023-12-23 23:30:09 +08:00
ManicSteiner
53d8fdd844 feat: PS2 BIP format 2023-12-21 13:50:20 +08:00
ManicSteiner
718ceb4c40 feat: ArcGPDA of PCSG00543 Saenai Heroine no Sodatekata Blessing Flowers 2023-12-19 15:52:13 +08:00
Crsky
240bb39f99 Add contributors section to README.md 2023-12-17 20:11:26 +08:00
Crsky
a52d2f3e25 Merge pull request #16 from ManicsteinerYX/master
Massive updates
2023-12-17 15:06:22 +08:00
ManicSteiner
0434623308 feat: SPC-PS2 Image Format 2023-12-16 21:46:17 +08:00
ManicSteiner
30b875efe8 feat: LBG/PS2-SPC Image Format improvement 2023-12-16 16:05:20 +08:00
ManicSteiner
da4b6574da feat: LBG/PS2-SPC Image Format 2023-12-16 15:27:26 +08:00
Manicsteiner
3443a2d16f feat: no-xtx spc archive support (#1)
* feat: extract no-xtx spc archive

* feat: revert xtx-spc support
2023-12-16 10:45:56 +08:00
ManicSteiner
d6259d4535 feat: GTF/Rozen Maiden PS3 Image archive 2023-12-15 22:44:38 +08:00
ManicSteiner
5292d3c9c8 feat: DAT/FARC MAGES Rozen Maiden PS3 archive 2023-12-14 22:58:43 +08:00
ManicSteiner
b1cc4eddf3 feat: KID DATRAW archive (failed) 2023-12-14 22:07:58 +08:00
ManicSteiner
da00eee168 feat: MAGES PS3/PSV Image Format encoding 2023-12-10 22:49:27 +08:00
ManicSteiner
d4cde67f43 feat: MAGES PS3/PSV Image Format decoding 2023-12-09 23:48:09 +08:00
ManicSteiner
8188874e81 feat: add support of Pia Carrot e Youkoso 4 Xbox360 archive 2023-12-07 22:52:36 +08:00
ManicSteiner
4a3f4562b0 feat: add support of Shin Hayarigami 2 PS3 archive 2023-12-07 22:18:05 +08:00
ManicSteiner
6efe8ad30b perf: edit arc 2023-12-07 21:42:38 +08:00
ManicSteiner
dad693ca9e feat: add support of Shin Hayarigami PS3 image 2023-12-07 00:29:26 +08:00
ManicSteiner
85962ae01f feat: add support of Shin Hayarigami PS3 archive 2023-12-06 23:58:24 +08:00
ManicSteiner
e55dd019fc feat: add sopport of Love Once PS3 archive 2023-12-06 22:52:55 +08:00
ManicSteiner
5a9a873c4b perf: change ARC20 into none-packed entry 2023-12-05 23:24:09 +08:00
ManicSteiner
797816f856 feat: support Rune Princess archive 2023-12-05 00:41:53 +08:00
ManicSteiner
df2ebbc7c5 auto delete pdb 2023-11-29 23:42:37 +08:00
ManicSteiner
7d4c61e91a create MAGES folder and move LAY, MPK into it 2023-11-29 23:20:50 +08:00
ManicSteiner
4a81140a2c add support of PSV mpk 2023-11-29 23:01:54 +08:00
Crsky
66dde11935 (Artemis): Implement NekoNyan PNG image format 2023-11-19 15:50:00 +08:00
55 changed files with 3079 additions and 242 deletions

25
.github/workflows/build.yml vendored Normal file
View File

@@ -0,0 +1,25 @@
name: .NET Framework Build
on: [push, pull_request]
jobs:
build:
runs-on: windows-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
with:
show-progress: false
- name: Add msbuild to PATH
uses: microsoft/setup-msbuild@v2
# Restore NuGet packages
- name: Restore
run: nuget restore
# Build the solution
- name: Build
run: msbuild /p:Configuration=Release /p:Platform="Any CPU" /p:TargetFrameworkVersion=v4.6.2 GARbro.sln
# Publish the artifacts
- name: Publish Artifact
if: success()
uses: actions/upload-artifact@v4
with:
name: GARbro-Mod-ci-build
path: bin/Release

View File

@@ -14,6 +14,8 @@
<TargetFrameworkProfile />
<SolutionDir Condition="$(SolutionDir) == '' Or $(SolutionDir) == '*Undefined*'">..\</SolutionDir>
<RestorePackages>true</RestorePackages>
<NuGetPackageImportStamp>
</NuGetPackageImportStamp>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
@@ -102,6 +104,9 @@
<Reference Include="System.Data" />
<Reference Include="System.Xml" />
<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="Abel\ArcARC.cs" />
@@ -123,12 +128,24 @@
<Compile Include="AdvSys\ImageGWD.cs" />
<Compile Include="Ail\ArcLNK2.cs" />
<Compile Include="AIRNovel\ArcAIR.cs" />
<Compile Include="Artemis\ImageNekoPNG.cs" />
<Compile Include="CsWare\AudioWAV.cs" />
<Compile Include="CsWare\ImageGDT.cs" />
<Compile Include="DigitalWorks\ArcPACPS2.cs" />
<Compile Include="DxLib\HuffmanDecoder.cs" />
<Compile Include="DxLib\WidgetDXA.xaml.cs">
<DependentUpon>WidgetDXA.xaml</DependentUpon>
</Compile>
<Compile Include="FC01\ArcBDT.cs" />
<Compile Include="FC01\ArcSCXA.cs" />
<Compile Include="FC01\BdtTables.cs" />
<Compile Include="GScripter\ArcDATA.cs" />
<Compile Include="Ism\ImagePNG.cs" />
<Compile Include="Kid\ArcDATRAW.cs" />
<Compile Include="Kid\ImageBIP.cs" />
<Compile Include="Kid\ImageBIParc.cs" />
<Compile Include="Kid\ImageLBG.cs" />
<Compile Include="Kid\ImageSPC.cs" />
<Compile Include="Kogado\ArcARC.cs" />
<Compile Include="Ice\ImageIBM.cs" />
<Compile Include="Ice\ScriptISD.cs" />
@@ -168,7 +185,17 @@
<Compile Include="Macromedia\AudioSND.cs" />
<Compile Include="Macromedia\DirectorFile.cs" />
<Compile Include="Macromedia\Palettes.cs" />
<Compile Include="MAGES\ArcARC20.cs" />
<Compile Include="MAGES\ArcFARC.cs" />
<Compile Include="MAGES\ArcGPDA.cs" />
<Compile Include="MAGES\ArcGTF.cs" />
<Compile Include="MAGES\ArcLoveOnce.cs" />
<Compile Include="MAGES\ImageBIN.cs" />
<Compile Include="Mugi\ArcBIN.cs" />
<Compile Include="Musica\ArcPAK.cs" />
<Compile Include="NipponIchi\ArcCASN.cs" />
<Compile Include="NipponIchi\ArcPSFS.cs" />
<Compile Include="NipponIchi\ImageNMT.cs" />
<Compile Include="NScripter\Script.cs" />
<Compile Include="Psp\ArcQPK.cs" />
<Compile Include="ScrPlayer\ImageIMG.cs" />
@@ -309,8 +336,8 @@
<Compile Include="MyAdv\ArcPAC.cs" />
<Compile Include="Nekopack\ArcNEKO2.cs" />
<Compile Include="Nekopack\ArcNEKO3.cs" />
<Compile Include="NitroPlus\ArcLAY.cs" />
<Compile Include="NitroPlus\ArcMPK.cs" />
<Compile Include="MAGES\ArcLAY.cs" />
<Compile Include="MAGES\ArcMPK.cs" />
<Compile Include="NitroPlus\ArcNPP.cs" />
<Compile Include="Noesis\ArcIGA.cs" />
<Compile Include="NonColor\ArcMinato.cs" />
@@ -1127,6 +1154,10 @@
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
<Page Include="DxLib\WidgetDXA.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
<Page Include="DxLib\WidgetSCR.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
@@ -1298,6 +1329,7 @@ exit 0</PreBuildEvent>
<PostBuildEvent>if not exist "$(TargetDir)\GameData" mkdir "$(TargetDir)\GameData"
xcopy "$(ProjectDir)\Resources\Formats.dat" "$(TargetDir)\GameData\" /D /Y &gt;NUL</PostBuildEvent>
</PropertyGroup>
<Import Project="..\packages\ZstdNet.1.4.5\build\ZstdNet.targets" Condition="Exists('..\packages\ZstdNet.1.4.5\build\ZstdNet.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.
<Target Name="BeforeBuild">

View File

@@ -0,0 +1,165 @@
//! \file ImageNekoPNG.cs
//! \date Sun Nov 19 15:40:46 2023
//! \brief NekoNyan PNG image format.
//
// Copyright (C) 2016 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;
using System.ComponentModel.Composition;
using System.IO;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Windows.Media.Imaging;
using System.Windows.Media;
using System.Windows;
namespace GameRes.Formats.Artemis
{
internal class NekoPNGMetaData : ImageMetaData
{
public int[] Offset;
public int[] Length;
}
[Export(typeof(ImageFormat))]
public class ImageNekoPNG : ImageFormat
{
public override string Tag { get { return "PNG"; } }
public override string Description { get { return "NekoNyan PNG image format"; } }
public override uint Signature { get { return 0x00000040; } }
public override ImageData Read (IBinaryStream file, ImageMetaData info)
{
var metadata = (NekoPNGMetaData)info;
var bitmap = new WriteableBitmap ((int)info.Width, (int)info.Height, ImageData.DefaultDpiX, ImageData.DefaultDpiY, PixelFormats.Bgra32, null);
bitmap.Lock ();
try
{
var output = bitmap.BackBuffer;
int stride = bitmap.BackBufferStride;
for (var i = 0; i < 8; i++)
{
file.Position = metadata.Offset[i];
var data = file.ReadBytes (metadata.Length[i]);
for (var j = 0; j < 16; j++)
data[j] -= (byte)(0x77 * j);
var slice_width = 0;
var slice_height = 0;
if (1 != WebPGetInfo (data, (UIntPtr)data.Length, ref slice_width, ref slice_height))
throw new InvalidFormatException("WebP image decoder failed.");
var slice_size = stride * slice_height;
if (IntPtr.Zero == WebPDecodeBGRAInto (data, (UIntPtr)data.Length, output, (UIntPtr)slice_size, stride))
throw new InvalidFormatException("WebP image decoder failed.");
output += slice_size;
}
bitmap.AddDirtyRect (new Int32Rect(0, 0, (int)info.Width, (int)info.Height));
}
finally
{
bitmap.Unlock();
}
bitmap.Freeze();
return new ImageData (bitmap, info);
}
public override ImageMetaData ReadMetaData (IBinaryStream file)
{
if (file.Signature != 0x40)
return null;
if (file.Length <= 0x40)
return null;
var offset = new int[8];
var length = new int[8];
for (var i = 0; i < 8; i++)
offset[i] = file.ReadInt32();
for (var i = 0; i < 8; i++)
length[i] = file.ReadInt32();
LibWebPLoader.Load();
var image_width = 0;
var image_height = 0;
for (var i = 0; i < 8; i++)
{
if (offset[i] < 64)
return null;
if (length[i] < 32)
return null;
if (offset[i] + length[i] > file.Length)
return null;
file.Position = offset[i];
var webp_header = file.ReadBytes (32);
for (var j = 0; j < 16; j++)
webp_header[j] -= (byte)(0x77 * j);
var slice_width = 0;
var slice_height = 0;
if (1 != WebPGetInfo (webp_header, (UIntPtr)webp_header.Length, ref slice_width, ref slice_height))
throw new InvalidFormatException ("WebP image decoder failed.");
image_width = Math.Max (image_width, slice_width);
image_height += slice_height;
}
return new NekoPNGMetaData
{
Width = (uint)image_width,
Height = (uint)image_height,
BPP = 32,
Offset = offset,
Length = length,
};
}
public override void Write (Stream file, ImageData bitmap)
{
throw new NotImplementedException("ImageNekoPNG.Write not implemented");
}
[DllImport("libwebp.dll", EntryPoint = "WebPGetInfo", CallingConvention = CallingConvention.Cdecl)]
public static extern int WebPGetInfo ([MarshalAs(UnmanagedType.LPArray)] byte[] data, UIntPtr data_size, ref int width, ref int height);
[DllImport("libwebp.dll", EntryPoint = "WebPDecodeBGRAInto", CallingConvention = CallingConvention.Cdecl)]
public static extern IntPtr WebPDecodeBGRAInto ([MarshalAs(UnmanagedType.LPArray)] byte[] data, UIntPtr data_size, IntPtr output_buffer, UIntPtr output_buffer_size, int output_stride);
}
internal static class LibWebPLoader
{
[DllImport("kernel32.dll", SetLastError = true, CallingConvention = CallingConvention.StdCall)]
static extern IntPtr LoadLibraryEx (string lpFileName, IntPtr hReservedNull, uint dwFlags);
static bool loaded = false;
const uint LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR = 0x00000100;
const uint LOAD_LIBRARY_SEARCH_SYSTEM32 = 0x00000800;
public static void Load ()
{
if (loaded)
return;
var folder = Path.GetDirectoryName (Assembly.GetExecutingAssembly().Location);
folder = Path.Combine (folder, (IntPtr.Size == 4) ? "x86" : "x64");
var fullPath = Path.Combine (folder, "libwebp.dll");
fullPath = Path.GetFullPath (fullPath);
var handle = LoadLibraryEx (fullPath, IntPtr.Zero, LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR | LOAD_LIBRARY_SEARCH_SYSTEM32);
if (IntPtr.Zero == handle)
throw new Win32Exception (Marshal.GetLastWin32Error());
loaded = true;
}
}
}

View File

@@ -1,53 +1,53 @@
//! \file ArcGXP.cs
//! \date Thu Jun 30 08:17:12 2016
//! \brief Astronauts resource archive.
//
// Copyright (C) 2016 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;
using GameRes.Utility;
namespace GameRes.Formats.Astronauts
{
[Export(typeof(ArchiveFormat))]
public class PakOpener : ArchiveFormat
{
public override string Tag { get { return "GXP"; } }
public override string Description { get { return "Astronauts resource archive"; } }
public override uint Signature { get { return 0x505847; } } // 'GXP'
public override bool IsHierarchic { get { return true; } }
public override bool CanWrite { get { return false; } }
private bool UseNameAsKey = false;
static readonly byte[] KnownKey = {
0x40, 0x21, 0x28, 0x38, 0xA6, 0x6E, 0x43, 0xA5, 0x40, 0x21, 0x28, 0x38, 0xA6, 0x43, 0xA5, 0x64,
0x3E, 0x65, 0x24, 0x20, 0x46, 0x6E, 0x74,
};
//! \file ArcGXP.cs
//! \date Thu Jun 30 08:17:12 2016
//! \brief Astronauts resource archive.
//
// Copyright (C) 2016 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;
using GameRes.Utility;
namespace GameRes.Formats.Astronauts
{
[Export(typeof(ArchiveFormat))]
public class PakOpener : ArchiveFormat
{
public override string Tag { get { return "GXP"; } }
public override string Description { get { return "Astronauts resource archive"; } }
public override uint Signature { get { return 0x505847; } } // 'GXP'
public override bool IsHierarchic { get { return true; } }
public override bool CanWrite { get { return false; } }
private bool UseNameAsKey = false;
static readonly byte[] KnownKey = {
0x40, 0x21, 0x28, 0x38, 0xA6, 0x6E, 0x43, 0xA5, 0x40, 0x21, 0x28, 0x38, 0xA6, 0x43, 0xA5, 0x64,
0x3E, 0x65, 0x24, 0x20, 0x46, 0x6E, 0x74,
};
public override ArcFile TryOpen(ArcView file)
{
UseNameAsKey = false;
@@ -58,76 +58,76 @@ namespace GameRes.Formats.Astronauts
arc = TryOpenGxpFile(file);
}
return arc;
}
private ArcFile TryOpenGxpFile (ArcView file)
{
int count = file.View.ReadInt32 (0x18);
if (!IsSaneCount (count))
}
private ArcFile TryOpenGxpFile (ArcView file)
{
int count = file.View.ReadInt32 (0x18);
if (!IsSaneCount (count))
return null;
string arcname = Path.GetFileName(file.Name);
byte[] arcname_bytes = Encoding.ASCII.GetBytes(arcname);
long base_offset = file.View.ReadInt64 (0x28);
uint entry_key = KnownKey[0] | (1u ^ KnownKey[1]) << 8 | (2u ^ KnownKey[2]) << 16 | (3u ^ KnownKey[3]) << 24;
string arcname = Path.GetFileName(file.Name);
byte[] arcname_bytes = Encoding.ASCII.GetBytes(arcname);
long base_offset = file.View.ReadInt64 (0x28);
uint entry_key = KnownKey[0] | (1u ^ KnownKey[1]) << 8 | (2u ^ KnownKey[2]) << 16 | (3u ^ KnownKey[3]) << 24;
if (UseNameAsKey)
{
uint arcname_key = (uint)(arcname_bytes[0] | (arcname_bytes[1 % arcname_bytes.Length]) << 8 | (arcname_bytes[2 % arcname_bytes.Length]) << 16 | (arcname_bytes[3 % arcname_bytes.Length]) << 24);
entry_key ^= arcname_key;
}
uint index_offset = 0x30;
var entry_buffer = new byte[0x100];
var dir = new List<Entry> (count);
for (int i = 0; i < count; ++i)
{
var entry_length = file.View.ReadUInt32 (index_offset) ^ entry_key;
if (entry_length < 0x20 || entry_length > 0x1000)
return null;
if (entry_length > entry_buffer.Length)
entry_buffer = new byte[entry_length];
if (entry_length != file.View.Read (index_offset, entry_buffer, 0, entry_length))
return null;
}
uint index_offset = 0x30;
var entry_buffer = new byte[0x100];
var dir = new List<Entry> (count);
for (int i = 0; i < count; ++i)
{
var entry_length = file.View.ReadUInt32 (index_offset) ^ entry_key;
if (entry_length < 0x20 || entry_length > 0x1000)
return null;
if (entry_length > entry_buffer.Length)
entry_buffer = new byte[entry_length];
if (entry_length != file.View.Read (index_offset, entry_buffer, 0, entry_length))
return null;
if (UseNameAsKey)
Decrypt(entry_buffer, entry_length, arcname_bytes);
else
Decrypt(entry_buffer, entry_length);
int name_length = LittleEndian.ToInt32 (entry_buffer, 0xC) * 2; // length in characters
if (name_length >= entry_length)
return null;
var name = Encoding.Unicode.GetString (entry_buffer, 0x20, name_length);
var entry = FormatCatalog.Instance.Create<Entry> (name);
entry.Offset = base_offset + LittleEndian.ToInt64 (entry_buffer, 0x18);
entry.Size = LittleEndian.ToUInt32 (entry_buffer, 4); // length is 64-bit actually
if (!entry.CheckPlacement (file.MaxOffset))
return null;
dir.Add (entry);
index_offset += entry_length;
}
return new ArcFile (file, this, dir);
}
public override Stream OpenEntry (ArcFile arc, Entry entry)
{
var data = arc.File.View.ReadBytes (entry.Offset, entry.Size);
Decrypt(entry_buffer, entry_length);
int name_length = LittleEndian.ToInt32 (entry_buffer, 0xC) * 2; // length in characters
if (name_length >= entry_length)
return null;
var name = Encoding.Unicode.GetString (entry_buffer, 0x20, name_length);
var entry = FormatCatalog.Instance.Create<Entry> (name);
entry.Offset = base_offset + LittleEndian.ToInt64 (entry_buffer, 0x18);
entry.Size = LittleEndian.ToUInt32 (entry_buffer, 4); // length is 64-bit actually
if (!entry.CheckPlacement (file.MaxOffset))
return null;
dir.Add (entry);
index_offset += entry_length;
}
return new ArcFile (file, this, dir);
}
public override Stream OpenEntry (ArcFile arc, Entry entry)
{
var data = arc.File.View.ReadBytes (entry.Offset, entry.Size);
if (UseNameAsKey)
{
string arcname = Path.GetFileName(arc.File.Name);
byte[] arcname_bytes = Encoding.ASCII.GetBytes(arcname);
Decrypt(data, entry.Size, arcname_bytes);
}
}
else
Decrypt(data, entry.Size);
return new BinMemoryStream (data, entry.Name);
}
static void Decrypt (byte[] data, uint length, byte[] key=null)
{
for (uint i = 0; i < length; ++i)
{
byte xorkey = (byte)(i ^ KnownKey[i % KnownKey.Length]);
Decrypt(data, entry.Size);
return new BinMemoryStream (data, entry.Name);
}
static void Decrypt (byte[] data, uint length, byte[] key=null)
{
for (uint i = 0; i < length; ++i)
{
byte xorkey = (byte)(i ^ KnownKey[i % KnownKey.Length]);
if(null != key)
xorkey ^= key[i % key.Length];
data[i] ^= xorkey;
}
}
}
}
xorkey ^= key[i % key.Length];
data[i] ^= xorkey;
}
}
}
}

View File

@@ -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)
@@ -57,9 +57,9 @@ namespace GameRes.Formats.Cri
backend.Position = 4;
var lzss = new LzssStream (backend);
var input = new SeekableStream (lzss);
var base_name = Path.GetFileNameWithoutExtension(file.Name);
try
{
var base_name = Path.GetFileNameWithoutExtension (file.Name);
using (var spc = new XtxIndexBuilder (input, base_name))
{
spc.ReadIndex (0);
@@ -71,8 +71,15 @@ namespace GameRes.Formats.Cri
}
catch
{
input.Dispose();
throw;
//input.Dispose();
//throw;
var dir = new List<Entry>();
var entry = Create<PackedEntry>(base_name);
entry.Offset = 0;
entry.Size = (uint)input.Length;
entry.Type = "image";
dir.Add(entry);
return new SpcArchive(file, this, dir, input);
}
}

View File

@@ -33,7 +33,7 @@ namespace GameRes.Formats.Cri
[Export(typeof(ImageFormat))]
public class SpcFormat : XtxFormat
{
public override string Tag { get { return "SPC"; } }
public override string Tag { get { return "SPC/Xbox360"; } }
public override string Description { get { return "CRI MiddleWare compressed texture format"; } }
public override uint Signature { get { return 0; } }

View File

@@ -35,20 +35,20 @@ namespace GameRes.Formats.DigitalWorks
{
public override string Tag { get { return "PAC/HED"; } }
public override string Description { get { return "Digital Works resource archive"; } }
public override uint Signature { get { return 0x43415050; } } // 'PPAC-PAC'
public override uint Signature { get { return 0; } } // 'PPAC-PAC'
public override bool IsHierarchic { get { return false; } }
public override bool CanWrite { get { return false; } }
public override ArcFile TryOpen (ArcView file)
{
if (!file.View.AsciiEqual (4, "-PAC"))
if (!file.View.AsciiEqual (0, "PPAC-PAC") && !file.View.AsciiEqual(0, "FANA_V1.0.0.0"))
return null;
var hed_name = Path.ChangeExtension (file.Name, "hed");
if (!VFS.FileExists (hed_name))
return null;
using (var hed = VFS.OpenView (hed_name))
{
if (!hed.View.AsciiEqual (0, "PPAC-HED"))
if (!hed.View.AsciiEqual (0, "PPAC-HED") && !hed.View.AsciiEqual(0, "FANA_V1.0.0.0"))
return null;
uint index_offset = 0x10;
const uint data_offset = 0x10;

View File

@@ -0,0 +1,98 @@
//! \file ArcPACPS2.cs
//! \date 2018 Sep 18
//! \brief Digital Works PS2 resource archive.
//
// Copyright (C) 2018 by morkt
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//
using System.Collections.Generic;
using System.ComponentModel.Composition;
using System.IO;
using GameRes.Compression;
namespace GameRes.Formats.DigitalWorks
{
[Export(typeof(ArchiveFormat))]
public class PacPS2Opener : ArchiveFormat
{
public override string Tag { get { return "PAC/PS2"; } }
public override string Description { get { return "Digital Works PS2 resource archive"; } }
public override uint Signature { get { return 0x434150; } } // 'PAC'
public override bool IsHierarchic { get { return false; } }
public override bool CanWrite { get { return false; } }
/**
Target games:
Cafe Little Wish SLPM-65294
F Fanatic SLPM-65296
*/
public override ArcFile TryOpen (ArcView file)
{
uint filename_index = file.View.ReadUInt32(4);
int count = file.View.ReadInt32(8);
uint index_offset = 0x0C;
if (!IsSaneCount (count))
return null;
var dir = new List<Entry> (count);
int i = 0;
while (i < count)
{
var name = file.View.ReadString (filename_index + i * 0x40, 0x40);
var entry = FormatCatalog.Instance.Create<PackedEntry> (name);
entry.Offset = file.View.ReadUInt32 (index_offset + i * 8);
entry.Size = file.View.ReadUInt32 (index_offset + i * 8 + 4);
if (!entry.CheckPlacement (file.MaxOffset))
return null;
dir.Add (entry);
i++;
}
return new ArcFile (file, this, dir);
}
public override Stream OpenEntry (ArcFile arc, Entry entry)
{
var pent = entry as PackedEntry;
if (null == pent)
return base.OpenEntry (arc, entry);
if (!pent.IsPacked)
{
if (!arc.File.View.AsciiEqual (entry.Offset, "LZS\0"))
return base.OpenEntry (arc, entry);
pent.IsPacked = true;
pent.UnpackedSize = arc.File.View.ReadUInt32 (entry.Offset+4);
}
var input = arc.File.CreateStream (entry.Offset+8, entry.Size-8);
bool embedded_lzs = (input.Signature & ~0xF0u) == 0x535A4C0F; // 'LZS'
var lzs = new LzssStream (input);
if (embedded_lzs)
{
var header = new byte[8];
lzs.Read (header, 0, 8);
pent.UnpackedSize = header.ToUInt32 (4);
lzs = new LzssStream (lzs);
}
return lzs;
}
}
}

View File

@@ -198,7 +198,7 @@ namespace GameRes.Formats.DxLib
}
}
byte[] Unpack (Stream stream)
protected byte[] Unpack (Stream stream)
{
using (var input = new ArcView.Reader (stream))
{
@@ -262,7 +262,7 @@ namespace GameRes.Formats.DxLib
}
}
List<Entry> ReadIndex (ArcView file, int version, byte[] key)
protected List<Entry> ReadIndex (ArcView file, int version, byte[] key)
{
DxHeader dx = null;
if (version <= 4)
@@ -271,7 +271,7 @@ namespace GameRes.Formats.DxLib
dx = ReadArcHeaderV6 (file, version, key);
if (null == dx || dx.DirTable >= dx.IndexSize || dx.FileTable >= dx.IndexSize)
return null;
using (var encrypted = file.CreateStream (dx.IndexOffset, dx.IndexSize))
using (var encrypted = file.CreateStream (dx.IndexOffset, (uint)dx.IndexSize))
using (var index = new EncryptedStream (encrypted, version >= 6 ? 0 : dx.IndexOffset, key))
using (var reader = IndexReader.Create (dx, version, index))
{
@@ -305,21 +305,23 @@ namespace GameRes.Formats.DxLib
IndexSize = LittleEndian.ToUInt32 (header, 0),
BaseOffset = LittleEndian.ToInt64 (header, 4),
IndexOffset = LittleEndian.ToInt64 (header, 0x0C),
FileTable = (uint)LittleEndian.ToInt64 (header, 0x14),
DirTable = (uint)LittleEndian.ToInt64 (header, 0x1C),
FileTable = LittleEndian.ToInt64 (header, 0x14),
DirTable = LittleEndian.ToInt64 (header, 0x1C),
CodePage = LittleEndian.ToInt32 (header, 0x24),
};
}
internal static void Decrypt (byte[] data, int index, int count, long offset, byte[] key)
{
int key_pos = (int)(offset % key.Length);
for (int i = 0; i < count; ++i)
{
data[index+i] ^= key[key_pos++];
if (key.Length == key_pos)
key_pos = 0;
}
if (key.Length == 0)
return;
int key_pos = (int)(offset % key.Length);
for (int i = 0; i < count; ++i)
{
data[index + i] ^= key[key_pos++];
if (key.Length == key_pos)
key_pos = 0;
}
}
public override ResourceScheme Scheme
@@ -333,9 +335,9 @@ namespace GameRes.Formats.DxLib
{
public long BaseOffset;
public long IndexOffset;
public uint IndexSize;
public uint FileTable;
public uint DirTable;
public long IndexSize;
public long FileTable;
public long DirTable;
public int CodePage;
}
@@ -361,8 +363,10 @@ namespace GameRes.Formats.DxLib
{
if (version <= 4)
return new IndexReaderV2 (header, version, input);
else if (version >= 6)
else if (version >= 6 && version < 8)
return new IndexReaderV6 (header, version, input);
else if (version >=8)
return new IndexReaderV8 (header, version, input);
else
throw new InvalidFormatException ("Not supported DX archive version.");
}
@@ -545,7 +549,7 @@ namespace GameRes.Formats.DxLib
: base (stream, leave_open)
{
m_key = key;
m_base_pos = (int)(base_position % m_key.Length);
m_base_pos = m_key.Length !=0 ?(int)(base_position % m_key.Length):0;
}
public override int Read (byte[] buffer, int offset, int count)
@@ -559,32 +563,48 @@ namespace GameRes.Formats.DxLib
public override int ReadByte ()
{
int key_pos = (int)((m_base_pos + Position) % m_key.Length);
int b = BaseStream.ReadByte();
if (-1 != b)
if (m_key.Length !=0)
{
b ^= m_key[key_pos];
int key_pos = (int)((m_base_pos + Position) % m_key.Length);
if (-1 != b)
{
b ^= m_key[key_pos];
}
}
return b;
}
public override void Write (byte[] buffer, int offset, int count)
{
int key_pos = (int)((m_base_pos + Position) % m_key.Length);
byte[] write_buf = new byte[count];
for (int i = 0; i < count; ++i)
if (m_key.Length != 0)
{
write_buf[i] = (byte)(buffer[offset+i] ^ m_key[key_pos++]);
if (m_key.Length == key_pos)
key_pos = 0;
int key_pos = (int)((m_base_pos + Position) % m_key.Length);
for (int i = 0; i < count; ++i)
{
write_buf[i] = (byte)(buffer[offset + i] ^ m_key[key_pos++]);
if (m_key.Length == key_pos)
key_pos = 0;
}
} else
{
write_buf = buffer;
}
BaseStream.Write (write_buf, 0, count);
}
public override void WriteByte (byte value)
{
int key_pos = (int)((m_base_pos + Position) % m_key.Length);
BaseStream.WriteByte ((byte)(value ^ m_key[key_pos]));
if(m_key.Length != 0)
{
int key_pos = (int)((m_base_pos + Position) % m_key.Length);
BaseStream.WriteByte((byte)(value ^ m_key[key_pos]));
} else
{
BaseStream.WriteByte ((byte)value);
}
}
}
}

View File

@@ -23,13 +23,47 @@
// IN THE SOFTWARE.
//
using GameRes.Formats.Strings;
using System;
using System.CodeDom;
using System.Collections.Generic;
using System.ComponentModel.Composition;
using System.IO;
using System.Linq;
using System.Runtime.Remoting.Messaging;
using System.Windows.Navigation;
using static GameRes.Formats.DxLib.Dx8Opener;
namespace GameRes.Formats.DxLib
{
internal class DXA8PackedEntry : PackedEntry {
public bool HuffmanCompressed { get; set; }
public uint HuffmanSize { get; set; }
public uint LZSize { get; set; }
}
internal class DxArchive8 : DxArchive
{
public byte huffmanMaxKB;
public DxArchive8(ArcView arc, ArchiveFormat impl, ICollection<Entry> dir, IDxKey enc, int version,byte huffmanKB) : base(arc, impl, dir, enc, version)
{
huffmanMaxKB = huffmanKB;
}
}
internal class DxHeaderV8 : DxHeader
{
public DXA8Flags Flags;
public byte HuffmanKB; // oddly used only in Compression process not in decompression.
//15 bytes of padding.
}
[Export(typeof(ArchiveFormat))]
public class Dx8Opener : DxOpener
{
@@ -41,30 +75,252 @@ namespace GameRes.Formats.DxLib
public Dx8Opener ()
{
Extensions = new[] { "bin" };
Extensions = new string[] { "dxa", "hud", "usi", "med", "dat", "bin", "bcx", "wolf" };
Signatures = new[] { 0x00085844u };
}
static readonly byte[] DefaultKey = new byte[] { 0xBE, 0xC8, 0x8A, 0xF5, 0x28, 0x50, 0xC9 };
//static readonly byte[] DefaultKey = new byte[] { 0xBE, 0xC8, 0x8A, 0xF5, 0x28, 0x50, 0xC9 };
DxScheme DefaultScheme = new DxScheme { KnownKeys = new List<IDxKey>() };
internal enum DXA8Flags : UInt32
{
DXA_FLAG_NO_KEY=1, //file is not encrypted
DXA_FLAG_NO_HEAD_PRESS=1<<1, //do not compress the header after compressing individual entries
}
[Serializable]
public class DXAOpts : ResourceOptions
{
public string Keyword;
}
public override ResourceOptions GetDefaultOptions()
{
return new DXAOpts
{
Keyword = Properties.Settings.Default.DXAPassword
};
}
public override ResourceOptions GetOptions(object widget)
{
if (widget is GUI.WidgetDXA)
{
return new DXAOpts
{
Keyword = ((GUI.WidgetDXA)widget).Password.Text
};
}
return GetDefaultOptions();
}
public override object GetAccessWidget()
{
return new GUI.WidgetDXA();
}
public override ArcFile TryOpen (ArcView file)
{
var dx = new DxHeader {
var dx = new DxHeaderV8 {
IndexSize = file.View.ReadUInt32 (4),
BaseOffset = file.View.ReadInt64 (8),
IndexOffset = file.View.ReadInt64 (0x10),
FileTable = (uint)file.View.ReadInt64 (0x18),
DirTable = (uint)file.View.ReadInt64 (0x20),
FileTable = file.View.ReadInt64 (0x18),
DirTable = file.View.ReadInt64 (0x20),
CodePage = file.View.ReadInt32 (0x28),
Flags = (DXA8Flags)file.View.ReadUInt32(0x2C),
HuffmanKB = file.View.ReadByte(0x30)
};
if (dx.DirTable >= dx.IndexSize || dx.FileTable >= dx.IndexSize)
return null;
var key = DefaultKey;
var index = file.View.ReadBytes (dx.IndexOffset, dx.IndexSize);
Decrypt (index, 0, index.Length, 0, key);
// decrypt-2
// decompress
return null;
DxKey8 key = null;
//FIXME: ReadBytes sets hard cap of filesize to 4GB.
var headerBuffer = file.View.ReadBytes(dx.IndexOffset, (uint)(file.MaxOffset-dx.IndexOffset));
bool isencrypted = (dx.Flags & DXA8Flags.DXA_FLAG_NO_KEY) == 0;
if (isencrypted)
{
var keyStr = Query<DXAOpts>(arcStrings.ZIPEncryptedNotice).Keyword;
key = new DxKey8(keyStr,dx.CodePage);
}
Decrypt(headerBuffer, 0, headerBuffer.Length, 0, key.Key);
//Decrypted but might be compressed
if ((dx.Flags & DXA8Flags.DXA_FLAG_NO_HEAD_PRESS) == 0)
{
byte[] huffmanBuffer = new byte[headerBuffer.Length];
byte[] lzBuffer;
headerBuffer.CopyTo(huffmanBuffer, 0);
//huffmanBuffer = headerBuffer;
HuffmanDecoder decoder = new HuffmanDecoder(huffmanBuffer, (ulong)huffmanBuffer.LongLength);
lzBuffer = decoder.Unpack();
MemoryStream lzStream = new MemoryStream(lzBuffer);
headerBuffer = Unpack(lzStream);
}
List<Entry> entries;
//There MAY be the case where the singular file is over 4GB, but it's very rare.
using (var reader = IndexReader.Create(dx, 8, new MemoryStream(headerBuffer)))
{
entries = reader.Read();
}
return new DxArchive8(file, this,entries ,key, 8,dx.HuffmanKB);
//retu rn null;
}
public override Stream OpenEntry(ArcFile arc, Entry entry)
{
Stream input = arc.File.CreateStream(entry.Offset, entry.Size);
var dx_arc = arc as DxArchive8;
if (null == dx_arc)
return input;
var dx_ent = (DXA8PackedEntry)entry;
long dec_offset = dx_ent.UnpackedSize;
var key = dx_arc.Encryption.GetEntryKey(dx_ent.Name);
input = new EncryptedStream(input, dec_offset, key);
byte[] tmpBuffer = new byte[dx_ent.Size];
input.Read(tmpBuffer, 0, tmpBuffer.Length);
if (dx_ent.HuffmanCompressed)
{
byte[] buffer = new byte[dx_ent.HuffmanSize];
byte[] outBuffer = new byte[dx_ent.IsPacked ? dx_ent.LZSize : dx_ent.UnpackedSize];
Array.Copy(tmpBuffer, buffer, dx_ent.HuffmanSize);
HuffmanDecoder decoder = new HuffmanDecoder(buffer,dx_ent.HuffmanSize);
byte[] partTmpBuffer = decoder.Unpack();
//returned buffer might be partial. Check if this is the case.
var outBufSize = dx_ent.IsPacked ? dx_ent.LZSize : dx_ent.UnpackedSize;
if(dx_arc.huffmanMaxKB != 0xff && outBufSize > dx_arc.huffmanMaxKB * 1024 * 2)
{
//What we have here is two huffmanMaxKB KB buffers, that constitute the beginning and end of file respectively.
Array.Copy(partTmpBuffer,0, outBuffer, 0,dx_arc.huffmanMaxKB*1024);
Array.Copy(partTmpBuffer,dx_arc.huffmanMaxKB*1024,outBuffer,outBuffer.Length-dx_arc.huffmanMaxKB*1024,dx_arc.huffmanMaxKB*1024);
//uncompressed part goes into middle.
Array.Copy(tmpBuffer, dx_ent.HuffmanSize, outBuffer, dx_arc.huffmanMaxKB * 1024, outBufSize - dx_arc.huffmanMaxKB * 1024 * 2);
tmpBuffer = outBuffer;
} else
{
//that is all that needs to be done.
tmpBuffer = partTmpBuffer;
}
}
if(dx_ent.IsPacked)
{
byte[] buffer = new byte[dx_ent.LZSize];
tmpBuffer.CopyTo(buffer, 0);
var tmpMemStream = new MemoryStream(buffer);
tmpBuffer = Unpack(tmpMemStream);
}
return new BinMemoryStream(tmpBuffer, entry.Name);
/*
if (!dx_ent.IsPacked)
return input;
using (input)
{
var data = Unpack(input);
return new BinMemoryStream(data, entry.Name);
}
*/
//return null;
}
}
internal sealed class IndexReaderV8 : IndexReader
{
readonly int m_entry_size;
public IndexReaderV8(DxHeader header, int version, Stream input) : base(header, version, input)
{
m_entry_size = 0x48;
}
private class DxDirectory
{
public long DirOffset;
public long ParentDirOffset;
public int FileCount;
public long FileTable;
}
DxDirectory ReadDirEntry()
{
var dir = new DxDirectory
{
DirOffset = m_input.ReadInt64(),
ParentDirOffset = m_input.ReadInt64(),
FileCount = (int)m_input.ReadInt64(),
FileTable = m_input.ReadInt64()
};
return dir;
}
protected override void ReadFileTable(string root, long table_offset)
{
m_input.Position = m_header.DirTable + table_offset;
var dir = ReadDirEntry();
if (dir.DirOffset != -1 && dir.ParentDirOffset != -1)
{
m_input.Position = m_header.FileTable + dir.DirOffset;
root = Path.Combine(root, ExtractFileName(m_input.ReadInt64()));
}
long current_pos = m_header.FileTable + dir.FileTable;
for (int i = 0; i < dir.FileCount; ++i)
{
m_input.Position = current_pos;
var name_offset = m_input.ReadInt64();
uint attr = (uint)m_input.ReadInt64();
m_input.Seek(0x18, SeekOrigin.Current);
var offset = m_input.ReadInt64();
if (0 != (attr & 0x10)) // FILE_ATTRIBUTE_DIRECTORY
{
if (0 == offset || table_offset == offset)
throw new InvalidFormatException("Infinite recursion in DXA directory index");
ReadFileTable(root, offset);
}
else
{
var size = m_input.ReadInt64();
var packed_size = m_input.ReadInt64();
var huffman_packed_size = m_input.ReadInt64();
var entry = FormatCatalog.Instance.Create<DXA8PackedEntry>(Path.Combine(root, ExtractFileName(name_offset)));
entry.Offset = m_header.BaseOffset + offset;
entry.UnpackedSize = (uint)size;
entry.IsPacked = -1 != packed_size;
entry.HuffmanCompressed = -1 != huffman_packed_size;
entry.HuffmanSize = (uint)huffman_packed_size;
entry.LZSize = (uint)packed_size;
//Huffman compression: huffman_packed_size will not exceed 2*HuffmanKB KB. The rest of data is uncompressed (as far as Huffman compressor is concerned).
//Add length of uncompressed data to entry.Size
if (entry.HuffmanCompressed)
{
var outBufSize = entry.IsPacked ? packed_size : size;
var dx8_hdr = (DxHeaderV8)m_header;
if (outBufSize > dx8_hdr.HuffmanKB * 1024 * 2)
{
huffman_packed_size += outBufSize - dx8_hdr.HuffmanKB * 1024 * 2;
}
}
if (entry.IsPacked||entry.HuffmanCompressed)
entry.Size = (uint)(huffman_packed_size!=-1 ? huffman_packed_size:packed_size);
else
entry.Size = (uint)size;
m_dir.Add(entry);
}
current_pos += m_entry_size;
}
}
}
}

View File

@@ -26,6 +26,8 @@
using System;
using System.Linq;
using System.Security.Cryptography;
using System.Text;
using System.Xml.Linq;
using GameRes.Utility;
namespace GameRes.Formats.DxLib
@@ -153,4 +155,78 @@ namespace GameRes.Formats.DxLib
throw new NotSupportedException ("SHA-256 key cannot be restored.");
}
}
[Serializable]
public class DxKey8 : DxKey
{
private int codepage;
public DxKey8(string password,int codepage) : base(password ?? "DXARC")
{
this.codepage = codepage;
}
public override byte[] GetEntryKey(string name)
{
var password = this.Password;
var path = name.Split('\\', '/');
password += string.Join("", path.Reverse().Select(n => n.ToUpperInvariant()));
return CreateKey(password);
}
protected override byte[] CreateKey(string keyword)
{
//from DxArchive.cpp
//first check if the keyword is too short
if (keyword.Length < 4)
{
keyword += "DXARC";
}
//first split string to bytes. Use original encoding as basis. Otherwise we would fail to decrypt that.
Encoding tgtEncoding = Encoding.GetEncoding(codepage);
byte[] tgtBytes = tgtEncoding.GetBytes(keyword);
byte[] oddBuffer = new byte[(tgtBytes.Length/2)+(tgtBytes.Length%2)]; int oddCounter = 0;
byte[] evenBuffer = new byte[(tgtBytes.Length/2)]; int evenCounter = 0;
for (int i=0; i<tgtBytes.Length;i+=2,oddCounter++)
{
oddBuffer[oddCounter] = tgtBytes[i];
}
for (int i = 1; i < tgtBytes.Length; i += 2, evenCounter++)
{
evenBuffer[evenCounter] = tgtBytes[i];
}
UInt32 crc_0, crc_1;
crc_0 = Crc32.Compute(oddBuffer, 0, oddCounter);
crc_1 = Crc32.Compute(evenBuffer, 0, evenCounter);
byte[] key = new byte[7];
byte[] crc_0_Bytes = BitConverter.GetBytes(crc_0),crc_1_Bytes=BitConverter.GetBytes(crc_1);
key[0] = crc_0_Bytes[0];
key[1] = crc_0_Bytes[1];
key[2] = crc_0_Bytes[2];
key[3] = crc_0_Bytes[3];
key[4] = crc_1_Bytes[0];
key[5] = crc_1_Bytes[1];
key[6] = crc_1_Bytes[2];
return key;
/*
string oddString, evenString;
oddString = string.Concat(keyword.Where((c, i) => i % 2 == 0));
evenString = string.Concat(keyword.Where((c, i) => (i+1) % 2 == 0));
UInt32 crc_0, crc_1;
crc_0 = Crc32.Compute(Encoding.ASCII.GetBytes(oddString), 0, oddString.Length);
crc_1 = Crc32.Compute(Encoding.ASCII.GetBytes(evenString), 0, evenString.Length); */
/*
using (var sha = SHA256.Create())
{
var bytes = Encodings.cp932.GetBytes(keyword);
return sha.ComputeHash(bytes);
} */
}
protected override string RestoreKey(byte[] key)
{
throw new NotSupportedException("CRC key cannot be restored.");
}
}
}

View File

@@ -0,0 +1,368 @@
//! \file HuffmanDecoder.cs
//! \date 2024 Aug 2
//! \brief Custom Huffman decoder for DXA archives.
//
// Copyright (C) 2017 by morkt - GetBits function
// Copyright (C) 2024 by MrSoup678
//
// 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.
//
//Original file is Huffman.cpp. Creator: 山田 巧 Date: 2018 Dec 16
using System;
using System.Diagnostics;
using System.IO;
using System.Runtime.CompilerServices;
using GameRes.Utility;
namespace GameRes.Formats.DxLib
{
internal class DXA8HuffmanNode
{
public UInt64 Weight;
public int bitNumber;
public byte[] bitArray; //32 bytes here
public int Index;
public int ParentNode; // index of parent node.
public int[] ChildNode; //two children nodes, -1 if not existent.
internal DXA8HuffmanNode()
{
bitArray = new byte[32];
ChildNode = new int[2];
}
}
internal sealed class HuffmanDecoder
{
byte[] m_input;
byte[] m_output;
int m_src;
byte m_bits;
int m_bit_count;
ulong m_readBytes;
byte m_readBits;
DXA8HuffmanNode[] nodes; //256+255 nodes
ulong originalSize;
ulong compressedSize;
ulong headerSize;
ulong srcSize;
//ushort token = 256;
public HuffmanDecoder (byte[] src,ulong srcSize)
{
m_input = src;
m_output = null;
this.srcSize = srcSize;
m_src = 0;
m_bit_count = 0;
m_readBytes = 0;
m_readBits = 0;
originalSize = compressedSize = headerSize = 0;
ushort[] weights = new ushort[256];
nodes = new DXA8HuffmanNode[256+255]; //256 data nodes, then 255 hierarchy nodes.
for (int i = 0; i < nodes.Length; i++)
{
nodes[i] = new DXA8HuffmanNode();
}
}
public byte[] Unpack ()
{
for (int i=0; i<nodes.Length; i++)
{
nodes[i].ParentNode = -1;
nodes[i].ChildNode[0] = -1;
nodes[i].ChildNode[1] = -1;
}
SetupWeights();
//check if compressedSize and src size match.
if (srcSize!=(compressedSize+headerSize))
{
throw new FileSizeException(String.Format("Supplied srcSize does not match with compressedSize+headerSize. Expected {0} got {1}",compressedSize+headerSize,srcSize));
}
m_output = new byte[originalSize];
CreateTree();
PopulateDataNodes();
DoUnpack();
return m_output;
}
private void DoUnpack()
{
var targetSize = originalSize;
byte[] compressedData = new byte[compressedSize];
Array.Copy(m_input, (long)headerSize, compressedData, 0, (long)(compressedSize));
int PressBitCounter=0, PressBitData=0, Index=0, NodeIndex=0;
int PressSizeCounter = 0;
ulong DestSizeCounter = 0;
int[] NodeIndexTable=new int[512];
{
ushort[] bitMask = new ushort[9];
for (int i = 0; i < 9; i++)
{
bitMask[i] = (ushort)((1<<i+1) - 1);
}
for (int i = 0; i < 512; i++)
{
NodeIndexTable[i] = -1;
for (int j = 0; j < 256 + 254; j++)
{
ushort BitArrayFirstBatch;
if (nodes[j].bitNumber > 9) continue;
BitArrayFirstBatch = (ushort)(nodes[j].bitArray[0] | (nodes[j].bitArray[1] << 8));
if ((i & bitMask[nodes[j].bitNumber - 1]) == (BitArrayFirstBatch & bitMask[nodes[j].bitNumber-1]))
{
NodeIndexTable[i] = j;
break;
}
}
}
}
PressBitData = compressedData[PressBitCounter];
for (DestSizeCounter = 0;DestSizeCounter < originalSize; DestSizeCounter++)
{
if (DestSizeCounter>= originalSize - 17)
{
NodeIndex = 510;
}
else
{
if (PressBitCounter==8)
{
PressSizeCounter++;
PressBitData = compressedData[PressSizeCounter];
PressBitCounter = 0;
}
PressBitData = (PressBitData | (compressedData[PressSizeCounter+1]<<(8-PressBitCounter))) & 0x1ff;
NodeIndex = NodeIndexTable[PressBitData];
PressBitCounter += nodes[NodeIndex].bitNumber;
if (PressBitCounter >= 16)
{
PressSizeCounter += 2;
PressBitCounter -= 16;
PressBitData = compressedData[PressSizeCounter] >> PressBitCounter;
}
else if (PressBitCounter >=8)
{
PressSizeCounter ++;
PressBitCounter -= 8;
PressBitData = compressedData[PressSizeCounter] >> PressBitCounter;
}
else
{
PressBitData >>= nodes[NodeIndex].bitNumber;
}
}
while (NodeIndex > 255)
{
if (PressBitCounter == 8)
{
PressSizeCounter++;
PressBitData = compressedData[PressSizeCounter];
PressBitCounter = 0;
}
Index = PressBitData & 1;
PressBitData >>= 1;
PressBitCounter++;
NodeIndex = nodes[NodeIndex].ChildNode[Index];
}
m_output[DestSizeCounter] = (byte)NodeIndex;
}
}
private void PopulateDataNodes()
{
//The data which is populated is path from root to target node in bits.
byte[] ScratchSpace = new byte[32];
int TempBitIndex, TempBitCount;
for (int i = 0; i < 256 + 254; i++) //root node is excluded.
{
nodes[i].bitNumber = 0;
TempBitIndex = 0;
TempBitCount = 0;
ScratchSpace[TempBitIndex] = 0;
for (int j = i; nodes[j].ParentNode!=-1;j = nodes[j].ParentNode)
{
if (TempBitCount == 8)
{
TempBitCount = 0;
TempBitIndex++;
ScratchSpace[TempBitIndex] = 0;
}
ScratchSpace[TempBitIndex] <<= 1;
ScratchSpace[TempBitIndex] |= (byte)nodes[j].Index;
TempBitCount++;
nodes[i].bitNumber++;
}
//path is now backwards (target to root). Populate BitPath from root to target.
int BitIndex=0, BitCount=0;
nodes[i].bitArray[BitIndex] = 0;
while (TempBitIndex >= 0)
{
if (BitCount == 8)
{
BitCount = 0;
BitIndex++;
nodes[i].bitArray[BitIndex] = 0;
}
nodes[i].bitArray[BitIndex] |= (byte)((ScratchSpace[TempBitIndex] & 1) << BitCount);
ScratchSpace[TempBitIndex] >>= 1;
TempBitCount--;
if (TempBitCount == 0)
{
TempBitIndex--;
TempBitCount = 8;
}
BitCount++;
}
}
}
private void SetupWeights()
{
int sizeA, sizeB;
byte BitNum;
byte Minus;
ushort SaveData;
ushort[] weights = new ushort[256];
sizeA = (int)GetBits(6) + 1;
originalSize = GetBits(sizeA);
sizeB = (int)GetBits(6)+1;
compressedSize = GetBits(sizeB);
BitNum = (byte)(((int)GetBits(3) + 1) * 2);
Minus = (byte)GetBits(1);
SaveData = (ushort)GetBits(BitNum);
weights[0] = SaveData;
for (int i = 1; i < 256; i++)
{
BitNum = (byte)(((int)GetBits(3) + 1) * 2);
Minus = (byte)GetBits(1);
SaveData = (ushort)GetBits(BitNum);
weights[i] = (ushort)(Minus == 1 ? weights[i - 1] - SaveData : weights[i - 1] + SaveData);
}
headerSize = GetReadBytes();
for (int i = 0;i < 256; i++)
{
nodes[i].Weight = weights[i];
}
}
void CreateTree()
{
int NodeNum=256, DataNum=256;
while (DataNum > 1)
{
int MinNode1 = -1;
int MinNode2 = -1;
int NodeIndex = 0;
for (int i = 0; i < DataNum; NodeIndex++) {
//don't do anything if we already have a parent set.
if (nodes[NodeIndex].ParentNode != -1) continue;
i++;
//we need to get the two lowest numbers for parenting.
if (MinNode1 == -1 || nodes[MinNode1].Weight > nodes[NodeIndex].Weight)
{
{
MinNode2 = MinNode1;
MinNode1 = NodeIndex;
}
} else if (MinNode2 == -1 || nodes[MinNode2].Weight > nodes[NodeIndex].Weight)
{
MinNode2 = NodeIndex;
}
}
nodes[NodeNum].ParentNode = -1;
nodes[NodeNum].Weight = nodes[MinNode1].Weight + nodes[MinNode2].Weight;
nodes[NodeNum].ChildNode[0] = MinNode1;
nodes[NodeNum].ChildNode[1] = MinNode2;
nodes[MinNode1].Index = 0;
nodes[MinNode2].Index = 1;
nodes[MinNode1].ParentNode = NodeNum;
nodes[MinNode2].ParentNode = NodeNum;
NodeNum++;
DataNum--;
}
}
ulong GetBits (int count)
{
ulong bits = 0;
for (int i = 0; i < count;i++)
{
if (0 == m_bit_count)
{
m_bits = m_input[m_src];
m_src++;
m_bit_count = 8;
}
//bits are read backwards.
bits |= ((ulong)((m_bits >> (7 - m_readBits)) & 1)) <<(count-1-i);
--m_bit_count;
m_readBits++;
if (m_readBits ==8)
{
m_readBits = 0;
m_readBytes++;
}
}
return bits;
}
ulong GetReadBytes()
{
return m_readBytes + (m_readBits != 0 ? 1ul : 0ul);
}
}
}

View File

@@ -0,0 +1,19 @@
<StackPanel x:Class="GameRes.Formats.GUI.WidgetDXA"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:GameRes.Formats.DxLib"
mc:Ignorable="d"
xmlns:s="clr-namespace:GameRes.Formats.Strings"
xmlns:p="clr-namespace:GameRes.Formats.Properties"
xmlns:dxa="clr-namespace:GameRes.Formats.DxLib">
<Label Content="{x:Static s:arcStrings.ArcTitleOrPassword}" HorizontalAlignment="Left"/>
<ComboBox Name="Title" Width="200" HorizontalAlignment="Left"
ItemsSource="{Binding RelativeSource={RelativeSource Self}, Path=DataContext}"
SelectedValue="{Binding ElementName=Password, Path=Text, Mode=TwoWay}" SelectedValuePath="Value"
DisplayMemberPath="Key"/>
<TextBox x:Name="Password" Width="200" HorizontalAlignment="Left" Margin="0,5,0,0"
Text="{Binding Source={x:Static p:Settings.Default}, Path=DXAPassword, Mode=OneWay}"/>
</StackPanel>

View File

@@ -0,0 +1,29 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using GameRes.Formats.DxLib;
namespace GameRes.Formats.GUI
{
/// <summary>
/// Logika interakcji dla klasy WidgetDXA.xaml
/// </summary>
public partial class WidgetDXA : StackPanel
{
public WidgetDXA()
{
InitializeComponent();
}
}
}

View File

@@ -65,6 +65,16 @@ namespace GameRes.Formats.BGI
return null;
if (0 != stream.ReadInt64())
return null;
if (flag == 0)
{
int stride = (int)width * ((bpp + 7) / 8);
var pixels = new byte[stride * (int)height];
int read = stream.Read(pixels, 0, pixels.Length);
if (read != pixels.Length)
return null;
}
return new BgiMetaData
{
Width = (uint)width,

View File

@@ -27,8 +27,10 @@ using System;
using System.Collections.Generic;
using System.ComponentModel.Composition;
using System.IO;
using GameRes.Utility;
using GameRes.Compression;
using System.Text;
using System.Linq;
using System.Text.RegularExpressions;
namespace GameRes.Formats.Eushully
{
@@ -63,15 +65,64 @@ namespace GameRes.Formats.Eushully
return null;
}
static internal string GetAAIName(string alf_name)
{
const string pattern = @"^(APPEND(?:[0-9]+)?)(?:_[0-9]+)?\.ALF$";
var match = Regex.Match(alf_name, pattern);
if (match.Success)
return match.Groups[1].Value;
return alf_name;
}
internal IEnumerable<string> GetIndexNames (string alf_name)
{
yield return "sys5ini.bin";
yield return "sys4ini.bin";
yield return "sys3ini.bin";
yield return Path.ChangeExtension (alf_name, "AAI");
yield return Path.ChangeExtension (GetAAIName(alf_name), "AAI");
}
Tuple<string, Dictionary<string, List<Entry>>> LastAccessedIndex;
internal class AGEArchiveInfo
{
public readonly byte[] signature;
public readonly int offset;
public readonly bool isNameUnicode;
public readonly bool isLZSSCompressed;
public AGEArchiveInfo(byte[] Signature, int Offset, bool IsNameUnicode, bool IsLZSSCompressed)
{
signature = Signature;
offset = Offset;
isNameUnicode = IsNameUnicode;
isLZSSCompressed = IsLZSSCompressed;
}
}
static AGEArchiveInfo[] infos =
{
new AGEArchiveInfo(Encoding.ASCII.GetBytes("S3IN"), 0x12C, false, false),
new AGEArchiveInfo(Encoding.ASCII.GetBytes("S3IC"), 0x134, false, true),
new AGEArchiveInfo(Encoding.ASCII.GetBytes("S3AC"), 0x114, false, true),
new AGEArchiveInfo(Encoding.ASCII.GetBytes("S4IC"), 0x134, false, true),
new AGEArchiveInfo(Encoding.ASCII.GetBytes("S4AC"), 0x114, false, true),
new AGEArchiveInfo(Encoding.Unicode.GetBytes("S5IC"), 0x224, true, true),
new AGEArchiveInfo(Encoding.Unicode.GetBytes("S5AC"), 0x21C, true, true)
};
static internal AGEArchiveInfo GetAGEArcInfo(ArcView view)
{
byte[] sig = view.View.ReadBytes(0, 8);
var siglow = sig.Take(4);
var res = infos.Where(i => Enumerable.SequenceEqual(i.signature, siglow));
if (res.Any()) return res.First();
res = infos.Where(i => Enumerable.SequenceEqual(i.signature, sig));
if (res.Any()) return res.First();
return null;
}
List<Entry> ReadIndex (string ini_file, string arc_name)
{
if (null == LastAccessedIndex
@@ -81,24 +132,21 @@ namespace GameRes.Formats.Eushully
using (var ini = VFS.OpenView (ini_file))
{
IBinaryStream index;
bool is_append = ini.View.AsciiEqual (0, "S4AC");
if (is_append || ini.View.AsciiEqual (0, "S4IC") || ini.View.AsciiEqual (0, "S3IC"))
AGEArchiveInfo info = GetAGEArcInfo (ini);
if (info == null) return null;
if (info.isLZSSCompressed)
{
uint offset = is_append ? 0x114u : 0x134u;
uint packed_size = ini.View.ReadUInt32 (offset);
var packed = ini.CreateStream (offset+4, packed_size);
var unpacked = new LzssStream (packed);
index = new BinaryStream (unpacked, ini_file);
}
else if (ini.View.AsciiEqual (0, "S3IN"))
{
index = ini.CreateStream (0x12C);
index = new BinaryStream(new LzssStream(ini.CreateStream(info.offset + 4, (uint)ini.View.ReadInt32(info.offset))), ini_file);
}
else
return null;
{
index = ini.CreateStream(info.offset);
}
using (index)
{
var file_table = ReadSysIni (index);
var file_table = ReadSysIni (index, info);
if (null == file_table)
return null;
LastAccessedIndex = Tuple.Create (ini_file, file_table);
@@ -110,7 +158,7 @@ namespace GameRes.Formats.Eushully
return dir;
}
internal Dictionary<string, List<Entry>> ReadSysIni (IBinaryStream index)
internal Dictionary<string, List<Entry>> ReadSysIni (IBinaryStream index, AGEArchiveInfo info)
{
int arc_count = index.ReadInt32();
if (!IsSaneCount (arc_count))
@@ -119,7 +167,8 @@ namespace GameRes.Formats.Eushully
var arc_list = new List<Entry>[arc_count];
for (int i = 0; i < arc_count; ++i)
{
var name = index.ReadCString (0x100);
string name = info.isNameUnicode ? index.ReadCString(0x200, Encoding.Unicode) : index.ReadCString(0x100);
var file_list = new List<Entry>();
file_table.Add (name, file_list);
arc_list[i] = file_list;
@@ -127,9 +176,10 @@ namespace GameRes.Formats.Eushully
int file_count = index.ReadInt32();
if (!IsSaneCount (file_count))
return null;
for (int i = 0; i < file_count; ++i)
{
var name = index.ReadCString (0x40);
string name = info.isNameUnicode ? index.ReadCString(0x80, Encoding.Unicode) : index.ReadCString(0x40);
int arc_id = index.ReadInt32();
if (arc_id < 0 || arc_id >= arc_list.Length)
return null;

View File

@@ -0,0 +1,50 @@
using System.Collections.Generic;
using System.ComponentModel.Composition;
namespace GameRes.Formats.FC01
{
[Export(typeof(ArchiveFormat))]
public class SCXAOpener : ArchiveFormat
{
public override string Tag { get { return "ARC/SCXA Pia Carrot e Youkoso 4 Xbox360"; } }
public override string Description { get { return "Pia Carrot e Youkoso 4 Xbox360 resource archive"; } }
public override uint Signature { get { return 0x41584353; } } // 'SCXA'
public override bool IsHierarchic { get { return false; } }
public override bool CanWrite { get { return false; } }
public override ArcFile TryOpen(ArcView file)
{
uint datastart = file.View.ReadUInt32(4);
int count = file.View.ReadInt32(8);
if (!IsSaneCount(count))
return null;
uint tnstart = (uint)count * 4 + 12;
var dir = new List<Entry>(count);
uint index_offset;
for (int i = 0; i < count; ++i)
{
uint namestart = file.View.ReadUInt32(i * 4 + 12);
uint fstart = file.View.ReadUInt32(tnstart + namestart);
uint flength = file.View.ReadUInt32(tnstart + namestart + 4);
index_offset = tnstart + namestart + 8;
byte c;
List<byte> namebyte = new List<byte>();
while (true)
{
c = file.View.ReadByte((long)index_offset);
if (c == 0 | index_offset > datastart) break;
namebyte.Add(c);
index_offset++;
}
var name = System.Text.Encoding.ASCII.GetString(namebyte.ToArray());
var entry = Create<Entry>(name);
entry.Offset = datastart + fstart;
entry.Size = flength;
if (!entry.CheckPlacement(file.MaxOffset))
return null;
dir.Add(entry);
}
return new ArcFile(file, this, dir);
}
}
}

View File

@@ -26,7 +26,11 @@
using System;
using System.ComponentModel.Composition;
using System.IO;
using System.Linq;
using System.Collections.Generic;
using System.Windows;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using GameRes.Utility;
namespace GameRes.Formats.Interheart
@@ -37,6 +41,7 @@ namespace GameRes.Formats.Interheart
public override string Tag { get { return "KG"; } }
public override string Description { get { return "Interheart image format"; } }
public override uint Signature { get { return 0x4B474347; } } // 'GCGK'
public override bool CanWrite { get { return true; } }
public override ImageMetaData ReadMetaData (IBinaryStream stream)
{
@@ -94,9 +99,78 @@ namespace GameRes.Formats.Interheart
return ImageData.Create (info, PixelFormats.Bgra32, null, pixels);
}
public override void Write (Stream file, ImageData image)
{
throw new System.NotImplementedException ("KgFormat.Write not implemented");
public override void Write (Stream file, ImageData image) {
int stride = (int)image.Width * 4;
byte[] bitmap = new byte[stride * image.Height];
{
var image_bgra = image.Bitmap.Format != PixelFormats.Bgra32
? new FormatConvertedBitmap(image.Bitmap, PixelFormats.Bgra32, null, 0)
: image.Bitmap;
image_bgra.CopyPixels(bitmap, stride, 0);
}
uint[] offset_table = new uint[image.Height];
var kg_data = new List<byte>();
for (int y = 0; y < image.Height; y++) {
offset_table[y] = (uint)kg_data.Count();
bitmap
.AsMemory()
.Slice(y * stride, stride)
.Chunk(4)
.Aggregate(new List<(byte alpha, List<byte[]> data)> { }, (chunks, x) => {
var pixel = x.ToArray();
void new_chunk () => chunks.Add((alpha: pixel[3], data: new List<byte[]> { pixel }));
if (!chunks.Any()) {
new_chunk();
}
else {
var (alpha, data) = chunks.Last();
if (pixel[3] != data.Last()[3] || data.Count >= 256) {
new_chunk();
}
else {
data.Add(pixel);
}
}
return chunks;
}
)
.ForEach(chunk => {
kg_data.Add(chunk.alpha);
kg_data.Add((byte)chunk.data.Count());
if (chunk.alpha > 0) {
kg_data.AddRange(chunk.data.SelectMany(p => new[] { p[2], p[1], p[0] }));
}
});
}
var bw = new BinaryWriter(file);
bw.Write(new byte[] { 0x47, 0x43, 0x47, 0x4B });
bw.Write((UInt16)image.Width);
bw.Write((UInt16)image.Height);
bw.Write((uint)kg_data.Count);
{
byte[] buf = new byte[offset_table.Length * sizeof(uint)];
Buffer.BlockCopy(offset_table, 0, buf, 0, buf.Length);
bw.Write(buf);
}
bw.Write(kg_data.ToArray());
}
}
static class Extensions {
public static IEnumerable<Memory<T>> Chunk<T>(this Memory<T> arr, int size) {
for (var i = 0; i < arr.Length / size + 1; i++) {
if (i * size < arr.Length) {
yield return arr.Slice(i * size, size);
}
}
}
}
}

View File

@@ -44,7 +44,7 @@ namespace GameRes.Formats.ISM
public override ArcFile TryOpen (ArcView file)
{
if (!file.View.AsciiEqual (4, "ARCHIVED"))
if (!file.View.AsciiEqual (4, "ARCHIVED") && !file.View.AsciiEqual(4, "ENGLISH "))
return null;
int count = file.View.ReadInt16 (0x0C);
if (!IsSaneCount (count))

View File

@@ -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)
{

150
ArcFormats/Kid/ArcDATRAW.cs Normal file
View File

@@ -0,0 +1,150 @@
using GameRes.Utility;
using System;
using System.Collections.Generic;
using System.ComponentModel.Composition;
using System.IO;
namespace GameRes.Formats.Kid
{
[Export(typeof(ArchiveFormat))]
public class DATRAWOpener : ArchiveFormat
{
public override string Tag { get { return "DAT/KID & MAGES PS2 DAT RAW"; } }
public override string Description { get { return "DAT/KID & MAGES PS2 DAT RAW"; } }
public override uint Signature { get { return 0; } } //actually zero
public override bool IsHierarchic { get { return false; } }
public override bool CanWrite { get { return false; } }
private static readonly uint dataEntryCount = 4096;
public override ArcFile TryOpen(ArcView file)
{
var archivename = Path.GetFileNameWithoutExtension(file.Name);
var dir = new List<Entry>();
for (int i = 0; i < dataEntryCount; i++)
{
uint offset = file.View.ReadUInt32(i * 8);
uint size = file.View.ReadUInt32(i * 8 + 4);
offset = offset * 2048 + 0x8000;
size *= 1024;
if (offset > file.MaxOffset || size > file.MaxOffset)
{
throw new InvalidFormatException();
}
if (size == 0) continue;
var entry = Create<Entry>(archivename + i.ToString("D5"));
entry.Offset = offset;
entry.Size = size;
dir.Add(entry);
}
if (dir.Count == 0)
return null;
return new ArcFile(file, this, dir);
}
/*public override Stream OpenEntry(ArcFile arc, Entry entry)
{
IBinaryStream input = arc.File.CreateStream(entry.Offset, entry.Size, entry.Name);
if (input.Signature == 0x535043) // 'CPS'
{
using (input)
return UnpackCps(input);
}
return input.AsStream;
}*/
Stream UnpackCps(IBinaryStream input)
{
var header = input.ReadHeader(0x10);
int packed_size = header.ToInt32(4);
int compression = header.ToUInt16(0xA);
int unpacked_size = header.ToInt32(0xC);
//input.Seek(-4, SeekOrigin.End);
input.Seek(packed_size - 4, SeekOrigin.Begin);
uint key_offset = input.ReadUInt32() - 0x7534682;
input.Position = key_offset;
uint key = input.ReadUInt32() + key_offset + 0x3786425;
var decryptor = new CpsTransform(packed_size, (int)key_offset, key);
using (var decoded = new InputCryptoStream(input.AsStream, decryptor))
using (var cps = new BinaryStream(decoded, input.Name))
{
var output = new byte[unpacked_size];
if ((compression & 1) != 0)
{
cps.ReadInt32();
UnpackLnd(cps, output);
}
else if ((compression & 2) != 0)
{
UnpackLnd16(cps, output);
}
else
{
cps.ReadInt32();
cps.Read(output, 0, unpacked_size);
}
return new BinMemoryStream(output);
}
}
internal static void UnpackLnd(IBinaryStream input, byte[] output)
{
int unpacked_size = output.Length;
int dst = 0;
while (dst < unpacked_size)
{
int ctl = input.ReadByte();
if (-1 == ctl)
break;
if ((ctl & 0x80) != 0)
{
if ((ctl & 0x40) != 0)
{
int count = (ctl & 0x1F) + 2;
if ((ctl & 0x20) != 0)
count += input.ReadUInt8() << 5;
count = Math.Min(count, unpacked_size - dst);
byte v = input.ReadUInt8();
for (int i = 0; i < count; ++i)
output[dst++] = v;
}
else
{
int count = ((ctl >> 2) & 0xF) + 2;
int offset = ((ctl & 3) << 8) + input.ReadUInt8() + 1;
count = Math.Min(count, unpacked_size - dst);
Binary.CopyOverlapped(output, dst - offset, dst, count);
dst += count;
}
}
else if ((ctl & 0x40) != 0)
{
int length = Math.Min((ctl & 0x3F) + 2, unpacked_size - dst);
int count = input.ReadUInt8();
input.Read(output, dst, length);
dst += length;
count = Math.Min(count * length, unpacked_size - dst);
if (count > 0)
{
Binary.CopyOverlapped(output, dst - length, dst, count);
dst += count;
}
}
else
{
int count = (ctl & 0x1F) + 1;
if ((ctl & 0x20) != 0)
count += input.ReadUInt8() << 5;
count = Math.Min(count, unpacked_size - dst);
input.Read(output, dst, count);
dst += count;
}
}
}
static void UnpackLnd16(IBinaryStream input, byte[] output)
{
throw new NotImplementedException("KID Lnd16 compression not implemented.");
}
}
}

207
ArcFormats/Kid/ImageBIP.cs Normal file
View File

@@ -0,0 +1,207 @@
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();
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(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;
long denominator = file.Length;
if (multi)
{
file.Seek(0xA2, SeekOrigin.Begin);
uint f2start = file.ReadUInt16();
denominator = f2start * 1024 + fstart + 0x100;
}
double oversize = (double)width * height * 4 / denominator;
file.Seek(0x90, SeekOrigin.Begin);
//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;
}
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.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;
// 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)
{
if (info == null)
throw new NotSupportedException(string.Format("Not BIP texture format."));
var bipheader = (BipImageMetaData)info;
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;
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; }
public bool Multi { get; set; }
}
}
}

View File

@@ -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");
}
}
}

111
ArcFormats/Kid/ImageLBG.cs Normal file
View File

@@ -0,0 +1,111 @@
using System;
using System.Collections.Generic;
using System.ComponentModel.Composition;
using System.Windows.Media.Imaging;
using System.Windows.Media;
using System.IO;
namespace GameRes.Formats.Kid
{
[Export(typeof(ImageFormat))]
public class LbgFormat : ImageFormat
{
public override string Tag { get { return "LBG/PS2-SPC"; } }
public override string Description { get { return "KID PS2 SPC Image Format"; } }
public override uint Signature { get { return 0; } } //real signature may exist below
public LbgFormat()
{
Extensions = new string[] { "", "lbg" };
// Actually LBG format can contain more than one image as lip for chara, well, "not implemented".
}
public override ImageMetaData ReadMetaData(IBinaryStream file)
{
uint header = file.ReadUInt32();
uint overheader = 0;
if (header != 0x2047424C) //LBG\x20
{
overheader = header;
if (overheader > 0x50) //usually 10 or 30, never seen any larger
return null;
file.Seek(overheader, SeekOrigin.Begin);
header = file.ReadUInt32();
if (header != 0x2047424C) //LBG\x20
return null;
}
int width = file.ReadInt16();
int height = file.ReadInt16();
if (width <= 0 || height <= 0)
return null;
return new LbgImageMetaData
{
Width = (uint)width,
Height = (uint)height,
OverHeader = overheader,
};
}
public override ImageData Read(IBinaryStream file, ImageMetaData info)
{
if (info == null)
throw new NotSupportedException(string.Format("Not LBG texture format."));
/*if (info.Width != 640)
{
throw new NotSupportedException(string.Format("Not 640 pixels width, may not work."));
}*/
var lbgheader = (LbgImageMetaData)info;
uint oversize = 0;
uint blocknum = info.Width / 126;
if (lbgheader.OverHeader != 0)
{
file.Position = 4;
uint filesize = file.ReadUInt32();
if (filesize == 0)
oversize = 8;
else
{
filesize -= 16;
oversize = (filesize - info.Width * info.Height * 4) / info.Height / (blocknum + 1);
}
}
file.Position = lbgheader.OverHeader + 0x10;
//List<byte> pixels = new List<byte>();
byte[] pixels = new byte[info.Width * info.Height * 4];
for (int blockcount = 0; blockcount <= blocknum; blockcount++)
{
for (int y = 0; y < info.Height; y++)
{
for (int x = 0; (blockcount != blocknum & x < 126) | (blockcount == blocknum & x < info.Width - blockcount * 126); x++)
{
var pixel = file.ReadBytes(4); //RGBA with wrong A
long target = (x + blockcount * 126) * 4 + y * info.Width * 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); //needs a test
}
if (oversize != 0)
file.ReadBytes((int)oversize);
}
}
return ImageData.Create(info, PixelFormats.Bgra32, null, pixels);
}
public override void Write(Stream file, ImageData bitmap)
{
throw new NotImplementedException("LbgFormat.Write not implemented");
}
}
class LbgImageMetaData : ImageMetaData
{
public uint OverHeader { get; set; }
}
}

View File

@@ -0,0 +1,44 @@
using GameRes.Compression;
using System.ComponentModel.Composition;
using System.IO;
namespace GameRes.Formats.Kid
{
[Export(typeof(ImageFormat))]
public class SpcFormat: LbgFormat
{
public override string Tag { get { return "SPC/PS2"; } }
public override string Description { get { return "PS2 CRI MiddleWare compressed texture format"; } }
public override uint Signature { get { return 0; } }
public SpcFormat()
{
Extensions = new string[] { "spc" };
}
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 lbg = new BinaryStream(input, stream.Name))
return base.ReadMetaData(lbg);
}
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 lbg = new BinaryStream(input, stream.Name))
return base.Read(lbg, info);
}
public override void Write(Stream file, ImageData image)
{
throw new System.NotImplementedException("SpcFormat.Write not implemented");
}
}
}

View File

@@ -479,7 +479,7 @@ NextEntry:
using (var writer = new BinaryWriter (output, Encoding.ASCII, true))
{
writer.Write (s_xp3_header);
if (2 == xp3_options.Version)
if (2 == xp3_options.Version || 3 == xp3_options.Version)
{
writer.Write ((long)0x17);
writer.Write ((int)1);
@@ -516,7 +516,7 @@ NextEntry:
&& !(scheme.StartupTjsNotEncrypted && VFS.IsPathEqualsToFileName (name, "startup.tjs"))
};
bool compress = compress_contents && ShouldCompressFile (entry);
using (var file = File.Open (name, FileMode.Open, FileAccess.Read))
using (var file = File.Open (name, FileMode.Open, FileAccess.Read, FileShare.Read))
{
if (!xp3entry.IsEncrypted || 0 == file.Length)
RawFileCopy (file, xp3entry, output, compress);
@@ -538,20 +538,46 @@ NextEntry:
callback (callback_count++, null, arcStrings.MsgWritingIndex);
long dir_pos = 0;
if (3 == xp3_options.Version)
{
foreach (var entry in dir)
{
header.Write ((uint)0x6e666e68); // "hnfn"
header.Write ((long)(4+2+entry.Name.Length*2));
header.Write ((uint)entry.Hash);
header.Write ((short)entry.Name.Length);
foreach (char c in entry.Name)
header.Write (c);
}
dir_pos = header.BaseStream.Position;
}
foreach (var entry in dir)
{
var entry_name = entry.Name;
if (3 == xp3_options.Version)
{
using (var md5 = MD5.Create())
{
var text_bytes = Encoding.Unicode.GetBytes(entry.Name.ToLowerInvariant());
var hash = md5.ComputeHash(text_bytes);
var sb = new StringBuilder(32);
for (int i = 0; i < hash.Length; ++i)
sb.AppendFormat("{0:x2}", hash[i]);
entry_name = sb.ToString();
}
}
header.BaseStream.Position = dir_pos;
header.Write ((uint)0x656c6946); // "File"
long header_size_pos = header.BaseStream.Position;
header.Write ((long)0);
header.Write ((uint)0x6f666e69); // "info"
header.Write ((long)(4+8+8+2 + entry.Name.Length*2));
header.Write ((long)(4+8+8+2 + entry_name.Length*2));
header.Write ((uint)(use_encryption ? 0x80000000 : 0));
header.Write ((long)entry.UnpackedSize);
header.Write ((long)entry.Size);
header.Write ((short)entry.Name.Length);
foreach (char c in entry.Name)
header.Write ((short)entry_name.Length);
foreach (char c in entry_name)
header.Write (c);
header.Write ((uint)0x6d676573); // "segm"

View File

@@ -4,6 +4,9 @@
xmlns:s="clr-namespace:GameRes.Formats.Strings"
xmlns:p="clr-namespace:GameRes.Formats.Properties"
xmlns:local="clr-namespace:GameRes.Formats.GUI">
<Grid.Resources>
<local:Xp3VersionConverter x:Key="Xp3VersionConverter"></local:Xp3VersionConverter>
</Grid.Resources>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
@@ -14,9 +17,10 @@
<StackPanel Orientation="Vertical" Margin="0,0,10,5">
<Label Content="{x:Static s:arcStrings.XP3LabelVersion}" Target="{Binding ElementName=Version}" Padding="4,0,0,5"/>
<ComboBox Name="Version" Width="80" HorizontalAlignment="Left" SelectedValuePath="Content"
SelectedValue="{Binding Source={x:Static p:Settings.Default}, Path=XP3Version, Mode=TwoWay}">
SelectedValue="{Binding Source={x:Static p:Settings.Default}, Path=XP3Version, Mode=TwoWay, Converter={StaticResource Xp3VersionConverter}}">
<ComboBoxItem Content="1"/>
<ComboBoxItem Content="2"/>
<ComboBoxItem Content="Z"/>
</ComboBox>
</StackPanel>
<StackPanel Orientation="Vertical" Margin="0,0,0,5">

View File

@@ -1,5 +1,8 @@
using System.Windows;
using System;
using System.Globalization;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
namespace GameRes.Formats.GUI
{
@@ -13,4 +16,31 @@ namespace GameRes.Formats.GUI
InitializeComponent ();
}
}
public class Xp3VersionConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
var val = (int)value;
switch (val)
{
case 1: return "1";
case 2: return "2";
case 3: return "Z";
default: throw new NotImplementedException();
}
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
var str = value.ToString();
switch (str)
{
case "1": return 1;
case "2": return 2;
case "Z": return 3;
default: throw new NotImplementedException();
}
}
}
}

View File

@@ -0,0 +1,50 @@
using System;
using System.Collections.Generic;
using System.ComponentModel.Composition;
using System.Text;
namespace GameRes.Formats.MAGES
{
[Export(typeof(ArchiveFormat))]
public class ARC20Opener : ArchiveFormat
{
public override string Tag { get { return "ARC/Princess Soft ARC20"; } }
public override string Description { get { return "Princess Soft PS2 resource archive"; } }
public override uint Signature { get { return 0x20435241; } } // 'ARC\x20'
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 filename_end = file.View.ReadUInt32(8);
var dir = new List<Entry>(count);
uint index_offset;
for (int i = 0; i < count; ++i)
{
index_offset = file.View.ReadUInt32(16 * i + 16);
byte c;
List<byte> namebyte = new List<byte>();
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 = sjis.GetString(namebyte.ToArray());
var entry = Create<Entry>(name);
entry.Offset = file.View.ReadUInt32(16 * i + 16 + 4) * 2048;
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);
}
}
}

View File

@@ -0,0 +1,48 @@
using System.Collections.Generic;
using System.ComponentModel.Composition;
using System.Text;
namespace GameRes.Formats.MAGES
{
[Export(typeof(ArchiveFormat))]
public class FARCOpener : ArchiveFormat
{
public override string Tag { get { return "DAT/FARC Rozen Maiden PS3 archive"; } }
public override string Description { get { return "MAGES Rozen Maiden Wechseln Sie Welt ab PS3 BLJM61120 archive"; } }
public override uint Signature { get { return 0x43524146; } } // 'FARC'
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(20);
if (!IsSaneCount(count))
return null;
uint datanamestart = (uint)count * 20 + 36;
var dir = new List<Entry>(count);
for (int i = 0; i < count; ++i)
{
long namepl = file.View.ReadUInt32(20 * i + 36 + 16);
long index_offset = datanamestart + namepl;
byte c;
List<byte> namebyte = new List<byte>();
while (true)
{
c = file.View.ReadByte(index_offset);
if (c == 0) break;
namebyte.Add(c);
index_offset++;
}
var sjis = Encoding.GetEncoding("Shift-JIS");
var name = sjis.GetString(namebyte.ToArray());
var entry = Create<Entry>(name);
entry.Offset = file.View.ReadUInt32(20 * i + 36);
entry.Size = file.View.ReadUInt32(20 * i + 36 + 4);
if (entry.Size == 0)
continue;
dir.Add(entry);
}
return new ArcFile(file, this, dir);
}
}
}

View File

@@ -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<Entry>(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<byte> namebyte = new List<byte>();
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<Entry>(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);
}
}
}

116
ArcFormats/MAGES/ArcGTF.cs Normal file
View File

@@ -0,0 +1,116 @@
using GameRes.Utility;
using System;
using System.Collections.Generic;
using System.ComponentModel.Composition;
using System.IO;
using System.Xml.Linq;
namespace GameRes.Formats.MAGES
{
[Export(typeof(ArchiveFormat))]
public class GTFOpener : ArchiveFormat
{
public override string Tag { get { return "GTF/Rozen Maiden PS3 Image archive"; } }
public override string Description { get { return "MAGES Rozen Maiden Wechseln Sie Welt ab PS3 BLJM61120 Image archive"; } }
public override uint Signature { get { return 0xFF000202; } } // 'FF000202'
public override bool IsHierarchic { get { return false; } }
public override bool CanWrite { get { return false; } }
public override ArcFile TryOpen(ArcView file)
{
int count = Binary.BigEndian(file.View.ReadInt32(8));
if (!IsSaneCount(count))
return null;
string filename = Path.GetFileNameWithoutExtension(file.Name);
var dir = new List<Entry>(count);
for (int i = 0; i < count; i++)
{
var entry = Create<Entry_RawImage>(filename + '_' + i.ToString());
entry.Offset = Binary.BigEndian(file.View.ReadUInt32(16 + 36 * i));
entry.Size = Binary.BigEndian(file.View.ReadUInt32(16 + 36 * i + 4));
entry.width = Binary.BigEndian(file.View.ReadUInt16(32 + 36 * i));
entry.height = Binary.BigEndian(file.View.ReadUInt16(32 + 36 * i + 2));
entry.Type = "image";
dir.Add(entry);
}
return new ArcFile(file, this, dir);
}
public override Stream OpenEntry(ArcFile arc, Entry entry)
{
var compentry = (Entry_RawImage)entry;
IBinaryStream input = arc.File.CreateStream(entry.Offset, entry.Size, entry.Name);
return ReadImageGTF(input, compentry.width, compentry.height);
}
internal class Entry_RawImage : Entry
{
public ushort width { get; set; }
public ushort height { get; set; }
}
/*[Export(typeof(ImageFormat))]
public class GTFFormat : ImageFormat
{
public override string Tag { get { return "MAGES PS3/PSV Image Format"; } }
public override string Description { get { return "MAGES PS3/PSV Image Format"; } }
public override uint Signature { get { return 0; } }
public override ImageMetaData ReadMetaData(IBinaryStream file)
{
throw new System.NotImplementedException();
}
public override ImageData Read(IBinaryStream file, ImageMetaData info)
{
throw new System.NotImplementedException();
}
public override void Write(Stream file, ImageData bitmap)
{
throw new System.NotImplementedException();
}
}*/
/// <summary>
/// This method returns MAGES/ImageBIN format Image data stream, please ensure it's decoding method exists.
/// </summary>
/// <param name="input">GTF or BIN raw ARGB32 data, start in the upper left corner, each row.</param>
/// <param name="width"></param>
/// <param name="height"></param>
/// <returns>MAGES/ImageBIN format Image data stream</returns>
static Stream ReadImageGTF(IBinaryStream input, ushort width, ushort height)
{
//List<byte> bytes = new List<byte>();
byte[] widths = BitConverter.GetBytes(width);
byte[] heights = BitConverter.GetBytes(height);
byte[] bpp = BitConverter.GetBytes((uint)32);
/*Stream output = new MemoryStream();
output.Write(widths, 0, widths.Length);
output.Write(heights, 0, heights.Length);
output.Write(bpp, 0, bpp.Length);
output.Write(input.ReadBytes((int)input.Length), 0, (int)input.Length);
byte[] bytes = new byte[output.Length];
output.Seek(0, SeekOrigin.Begin);
output.Read(bytes, 0, bytes.Length);
output.Dispose();*/
byte[] inputData = input.ReadBytes((int)input.Length);
byte[] outputData = new byte[widths.Length + heights.Length + bpp.Length + inputData.Length];
Buffer.BlockCopy(widths, 0, outputData, 0, widths.Length);
Buffer.BlockCopy(heights, 0, outputData, widths.Length, heights.Length);
Buffer.BlockCopy(bpp, 0, outputData, widths.Length + heights.Length, bpp.Length);
Buffer.BlockCopy(inputData, 0, outputData, widths.Length + heights.Length + bpp.Length, inputData.Length);
return new BinMemoryStream(outputData);
/*List<byte> pixels = new List<byte>();
for (int i = 0; i < input.Length; i++)
{
var pixel = input.ReadBytes(4); //ARGB
//BGRA
pixels.Add(pixel[3]);
pixels.Add(pixel[2]);
pixels.Add(pixel[1]);
pixels.Add(pixel[0]);
}*/
}
}
}

View File

@@ -32,7 +32,7 @@ using System.Windows;
using System.Windows.Media;
using System.Windows.Media.Imaging;
namespace GameRes.Formats.NitroPlus
namespace GameRes.Formats.MAGES
{
internal class LayEntry : Entry
{

View File

@@ -0,0 +1,57 @@
using System.Collections.Generic;
using System.ComponentModel.Composition;
using System.IO;
using System.Runtime.CompilerServices;
using System.Xml.Linq;
namespace GameRes.Formats.MAGES
{
[Export(typeof(ArchiveFormat))]
public class LoveOnceOpener : ArchiveFormat
{
public override string Tag { get { return "BIN/LoveOnce"; } }
public override string Description { get { return "L@ve Once PS3 resource archive"; } }
public override uint Signature { get { return 0; } } // no header
public override bool IsHierarchic { get { return false; } }
public override bool CanWrite { get { return false; } }
public override ArcFile TryOpen(ArcView filedata)
{
ArcView filelist;
if (filedata.Name.EndsWith("data.bin"))
{
string filepath = Path.GetDirectoryName(filedata.Name);
filepath = Path.Combine(filepath, "list.bin");
if (! File.Exists(filepath)) return null;
filelist = new ArcView(filepath);
}
else if (filedata.Name.EndsWith("list.bin"))
{
string filepath = Path.GetDirectoryName(filedata.Name);
filepath = Path.Combine(filepath, "data.bin");
if (!File.Exists(filepath)) return null;
filelist = new ArcView(filepath);
return TryOpen(filelist);
}
else return null;
uint index_offset = 0, count = 0;
var dir = new List<Entry>();
while (index_offset < filelist.MaxOffset)
{
uint filesize = filelist.View.ReadUInt32(index_offset);
uint fileoffset = filelist.View.ReadUInt32(index_offset + 8);
string name = filelist.View.ReadString(index_offset + 0x10, 0x40);
var entry = Create<Entry>(name);
entry.Size = filesize;
entry.Offset = fileoffset;
if (!entry.CheckPlacement(filedata.MaxOffset))
return null;
dir.Add(entry);
count++;
index_offset += 0x50;
}
return new ArcFile(filedata, this, dir);
}
}
}

View File

@@ -26,7 +26,7 @@
using System.Collections.Generic;
using System.ComponentModel.Composition;
namespace GameRes.Formats.NitroPlus
namespace GameRes.Formats.MAGES
{
[Export(typeof(ArchiveFormat))]
public class MpkOpener : ArchiveFormat
@@ -39,22 +39,43 @@ namespace GameRes.Formats.NitroPlus
public override ArcFile TryOpen (ArcView file)
{
uint version_temp = file.View.ReadUInt32 (4);
int count = file.View.ReadInt32 (8);
if (!IsSaneCount (count))
return null;
uint index_offset = 0x48;
var dir = new List<Entry> (count);
for (int i = 0; i < count; ++i)
uint index_offset = 0;
if (version_temp == 65536)
{
var name = file.View.ReadString (index_offset+0x18, 0xE0);
var entry = Create<PackedEntry> (name);
entry.Offset = file.View.ReadInt64 (index_offset);
entry.Size = file.View.ReadUInt32 (index_offset+8);
entry.UnpackedSize = file.View.ReadUInt32 (index_offset+0x10);
if (!entry.CheckPlacement (file.MaxOffset))
return null;
dir.Add (entry);
index_offset += 0x100;
index_offset = 0x44;
for (int i = 0; i < count; ++i)
{
var name = file.View.ReadString (index_offset+0x1c, 0xE0);
var entry = Create<PackedEntry> (name);
entry.Offset = file.View.ReadInt32 (index_offset);
entry.Size = file.View.ReadUInt32 (index_offset+4);
entry.UnpackedSize = file.View.ReadUInt32 (index_offset+8);
if (!entry.CheckPlacement (file.MaxOffset))
return null;
dir.Add (entry);
index_offset += 0x100;
}
}
else
{
index_offset = 0x48;
for (int i = 0; i < count; ++i)
{
var name = file.View.ReadString (index_offset+0x18, 0xE0);
var entry = Create<PackedEntry> (name);
entry.Offset = file.View.ReadInt64 (index_offset);
entry.Size = file.View.ReadUInt32 (index_offset+8);
entry.UnpackedSize = file.View.ReadUInt32 (index_offset+0x10);
if (!entry.CheckPlacement (file.MaxOffset))
return null;
dir.Add (entry);
index_offset += 0x100;
}
}
return new ArcFile (file, this, dir);
}

View File

@@ -0,0 +1,143 @@
using System;
using System.Collections.Generic;
using System.ComponentModel.Composition;
using System.Diagnostics.Eventing.Reader;
using System.IO;
using System.Text;
using System.Windows;
using System.Windows.Media;
using System.Windows.Media.Imaging;
namespace GameRes.Formats.MAGES
{
[Export(typeof(ImageFormat))]
public class BinFormat : ImageFormat
{
public override string Tag { get { return "MAGES PS3/PSV Image Format"; } }
public override string Description { get { return "MAGES PS3/PSV Image Format"; } }
public override uint Signature { get { return 0; } }
public override bool CanWrite { get { return true; } }
public BinFormat()
{
Extensions = new string[] { "" };
}
public override ImageMetaData ReadMetaData(IBinaryStream file)
{
//var header = file.ReadHeader(8);
int width = file.ReadInt16();
int height = file.ReadInt16();
if (width <= 0 || height <= 0)
return null;
int bpp = file.ReadInt16();
if (32 == bpp)
{
uint imagedatasize = (uint)width * (uint)height * 4;
if (file.Length != imagedatasize + 8)
return null;
}
else if (8 == bpp)
{
uint imagedatasize = (uint)width * (uint)height + 256;
if (file.Length != imagedatasize + 8)
return null;
}
else
return null;
return new ImageMetaData
{
Width = (uint)width,
Height = (uint)height,
BPP = bpp,
};
}
public override ImageData Read(IBinaryStream file, ImageMetaData info)
{
if (info == null)
throw new NotSupportedException(string.Format("Not BIN texture format."));
if (info.BPP == 32)
{
uint pixelnum = info.Width * info.Height;
if (file.Length != pixelnum * 4 + 8) throw new NotSupportedException(string.Format("Not BIN 32ARGB texture format."));
file.Position = 8;
//var data = file.ReadBytes(info.iWidth * info.iHeight * 4);
List<byte> pixels = new List<byte>();
for (int i = 0; i < pixelnum; i++)
{
var pixel = file.ReadBytes(4); //ARGB
//BGRA
pixels.Add(pixel[3]);
pixels.Add(pixel[2]);
pixels.Add(pixel[1]);
pixels.Add(pixel[0]);
}
return ImageData.Create(info, PixelFormats.Bgra32, null, pixels.ToArray());
}
else if (info.BPP == 8)
{
uint imagedatasize = info.Width * info.Height + 256;
if (file.Length != imagedatasize + 8) throw new NotSupportedException(string.Format("Not BIN 256colors texture format."));
file.Position = 8;
//var pixelColor = file.ReadBytes(256 * 4);
List<Color> colors = new List<Color>();
for (int i = 0; i < 256; i += 4)
{
Color c = new Color();
var color_b = file.ReadBytes(4); //BGRA
c.B = color_b[0];
c.G = color_b[1];
c.R = color_b[2];
c.A = color_b[3];
colors.Add(c);
}
BitmapPalette palette = new BitmapPalette(colors);
//file.Position += 256 * 4;
var data = file.ReadBytes(info.iWidth * info.iHeight);
return ImageData.Create(info, PixelFormats.Indexed8, palette, data);
}
else
throw new NotSupportedException(string.Format("Not BIN texture format."));
}
public override void Write(Stream stream, ImageData image)
{
//throw new System.NotImplementedException("BINFormat.Write not implemented");
using (var file = new BinaryWriter(stream, Encoding.ASCII, true))
{
if (image.Width > ushort.MaxValue || image.Height > ushort.MaxValue)
{
throw new NotSupportedException(string.Format("Image width or height oversize."));
}
if (image.BPP != 32)
{
throw new NotSupportedException(string.Format("Image bitdepth not supported, should be 32."));
}
file.Write((ushort)image.Width);
file.Write((ushort)image.Height);
file.Write(image.BPP);
var bitmap = image.Bitmap;
if (bitmap.Format != PixelFormats.Bgra32)
{
bitmap = new FormatConvertedBitmap(image.Bitmap, PixelFormats.Bgra32, null, 0);
}
int stride = (int)image.Width * 4;
byte[] row_data = new byte[stride];
Int32Rect rect = new Int32Rect(0, 0, (int)image.Width, 1);
for (uint row = 0; row < image.Height; ++row)
{
bitmap.CopyPixels(rect, row_data, stride, 0);
for (uint col = 0; col < image.Width; ++col)
{
(row_data[(col * 4) + 3], row_data[col * 4]) = (row_data[col * 4], row_data[(col * 4) + 3]);
(row_data[(col * 4) + 2], row_data[(col * 4) + 1]) = (row_data[(col * 4) + 1], row_data[(col * 4) + 2]);
}
file.Write(row_data);
rect.Y++;
}
}
}
}
}

173
ArcFormats/Musica/ArcPAK.cs Normal file
View File

@@ -0,0 +1,173 @@
using System;
using System.Collections.Generic;
using System.ComponentModel.Composition;
using System.IO;
using System.Text;
namespace GameRes.Formats.Musica
{
[Export(typeof(ArchiveFormat))]
public class PakOpener : ArchiveFormat
{
public override string Tag { get; } = "PAK";
public override string Description { get; } = "Musica engine legacy resource archive";
public override uint Signature { get; } = 0;
public override bool IsHierarchic { get; } = true;
public override bool CanWrite { get; } = false;
public PakOpener()
{
Extensions = new[] { "pak" };
ContainedFormats = new string[] { "PNG", "OGG" };
}
static readonly HashSet<string> PakImageNames = new HashSet<string>()
{
"bg", "st",
};
static readonly HashSet<string> PakAudioNames = new HashSet<string>()
{
"bgm", "se", "voice",
};
private string GetType(string pakName, string entryName)
{
if (PakImageNames.Contains(pakName))
{
return "image";
}
if (PakAudioNames.Contains(pakName))
{
return "audio";
}
return FormatCatalog.Instance.GetTypeFromName(entryName, ContainedFormats);
}
public override ArcFile TryOpen(ArcView view)
{
Stream input = view.CreateStream();
using(input = new NegStream(input))
{
using(ArcView.Reader reader = new ArcView.Reader(input))
{
int count = reader.ReadInt32();
if (count <= 0)
{
return null;
}
List<Entry> entries = new List<Entry>(count);
for(int i = 0; i < count; ++i)
{
uint indexLen = reader.ReadUInt32();
long indexStart = input.Position;
string name = input.ReadCString();
if (string.IsNullOrWhiteSpace(name))
{
return null;
}
uint size = reader.ReadUInt32();
uint offset = reader.ReadUInt32();
Entry entry = new Entry() { Name = name, Offset = offset, Size = size };
if (!entry.CheckPlacement(view.MaxOffset))
{
return null;
}
entry.Type = this.GetType(Path.GetFileNameWithoutExtension(view.Name), entry.Name);
entries.Add(entry);
input.Position = indexStart + indexLen;
}
return new PakArchive(view, this, entries, input.Position);
}
}
}
public override Stream OpenEntry(ArcFile arc, Entry entry)
{
if (!(arc is PakArchive pakArc))
{
return base.OpenEntry(arc, entry);
}
return new NegStream(base.OpenEntry(arc, pakArc.GetEntry(entry)));
}
}
internal class PakArchive : ArcFile
{
private long m_IndexSize;
public PakArchive(ArcView arc, ArchiveFormat impl, ICollection<Entry> dir, long indexSize) : base(arc, impl, dir)
{
m_IndexSize = indexSize;
}
public Entry GetEntry(Entry e)
{
return new Entry
{
Name = e.Name,
Offset = e.Offset + m_IndexSize,
Size = e.Size,
Type = e.Type,
};
}
}
//neg reg8
public class NegStream : ProxyStream
{
public NegStream(Stream stream, bool leave_open = false) : base(stream, leave_open)
{
}
public override int Read(byte[] buffer, int offset, int count)
{
int read = BaseStream.Read(buffer, offset, count);
for (int i = 0; i < read; ++i)
{
buffer[offset + i] = (byte)-buffer[offset + i];
}
return read;
}
public override int ReadByte()
{
int b = BaseStream.ReadByte();
if (-1 != b)
{
b = (byte)-b;
}
return b;
}
byte[] write_buf;
public override void Write(byte[] buffer, int offset, int count)
{
if (null == write_buf)
write_buf = new byte[81920];
while (count > 0)
{
int chunk = Math.Min(write_buf.Length, count);
for (int i = 0; i < chunk; ++i)
{
write_buf[i] = (byte)-buffer[offset + i];
}
BaseStream.Write(write_buf, 0, chunk);
offset += chunk;
count -= chunk;
}
}
public override void WriteByte(byte value)
{
BaseStream.WriteByte((byte)-value);
}
}
}

View File

@@ -34,11 +34,13 @@ namespace GameRes.Formats.Musica
internal class SqzArchive : ArcFile
{
public readonly ImageMetaData Info;
public readonly int FPS;
public SqzArchive (ArcView arc, ArchiveFormat impl, ICollection<Entry> dir, ImageMetaData info)
public SqzArchive (ArcView arc, ArchiveFormat impl, ICollection<Entry> dir, ImageMetaData info, int fps)
: base (arc, impl, dir)
{
Info = info;
FPS = fps;
}
}
@@ -53,7 +55,7 @@ namespace GameRes.Formats.Musica
public override ArcFile TryOpen (ArcView file)
{
int count = file.View.ReadInt32 (0x10) * 2;
int count = file.View.ReadInt32 (4);
if (!IsSaneCount (count))
return null;
@@ -62,8 +64,8 @@ namespace GameRes.Formats.Musica
Width = file.View.ReadUInt32 (8),
Height = file.View.ReadUInt32 (0xC),
BPP = 32,
// BPP = file.View.ReadInt32 (4),
};
int fps = file.View.ReadInt32(0x10);
var base_name = Path.GetFileNameWithoutExtension (file.Name);
uint index_offset = 0x14;
var dir = new List<Entry> (count);
@@ -80,7 +82,7 @@ namespace GameRes.Formats.Musica
dir.Add (entry);
index_offset += 8;
}
return new SqzArchive (file, this, dir, info);
return new SqzArchive (file, this, dir, info, fps);
}
public override IImageDecoder OpenImage (ArcFile arc, Entry entry)

View File

@@ -42,6 +42,9 @@ namespace GameRes.Formats.NeXAS
Huffman,
Deflate,
DeflateOrNone,
None2,
Zstd,
ZstdOrNone,
}
public class PacArchive : ArcFile
@@ -67,13 +70,16 @@ namespace GameRes.Formats.NeXAS
public PacOpener ()
{
Signatures = new uint[] { 0x00434150, 0 };
Settings = new[] { PacEncoding };
}
EncodingSetting PacEncoding = new EncodingSetting ("NexasEncodingCP", "DefaultEncoding");
public override ArcFile TryOpen (ArcView file)
{
if (!file.View.AsciiEqual (0, "PAC") || 'K' == file.View.ReadByte (3))
return null;
var reader = new IndexReader (file);
var reader = new IndexReader (file, PacEncoding.Get<Encoding>());
var dir = reader.Read();
if (null == dir)
return null;
@@ -88,16 +94,18 @@ namespace GameRes.Formats.NeXAS
ArcView m_file;
int m_count;
int m_pack_type;
Encoding m_encoding;
const int MaxNameLength = 0x40;
public Compression PackType { get { return (Compression)m_pack_type; } }
public IndexReader (ArcView file)
public IndexReader (ArcView file, Encoding enc)
{
m_file = file;
m_count = file.View.ReadInt32 (4);
m_pack_type = file.View.ReadInt32 (8);
m_encoding = enc;
}
List<Entry> m_dir;
@@ -151,7 +159,7 @@ namespace GameRes.Formats.NeXAS
m_dir.Clear();
for (int i = 0; i < m_count; ++i)
{
var name = index.ReadCString (name_length);
var name = index.ReadCString (name_length, m_encoding);
if (string.IsNullOrWhiteSpace (name))
return false;
var entry = FormatCatalog.Instance.Create<PackedEntry> (name);
@@ -160,7 +168,23 @@ namespace GameRes.Formats.NeXAS
entry.Size = index.ReadUInt32();
if (!entry.CheckPlacement (m_file.MaxOffset))
return false;
entry.IsPacked = m_pack_type != 0 && (m_pack_type != 4 || entry.Size != entry.UnpackedSize);
switch (m_pack_type)
{
case 1:
case 2:
case 3:
case 6:
{
entry.IsPacked = true;
break;
}
case 4:
case 7:
{
entry.IsPacked = entry.Size != entry.UnpackedSize;
break;
}
}
m_dir.Add (entry);
}
return true;
@@ -188,8 +212,16 @@ namespace GameRes.Formats.NeXAS
return new BinMemoryStream (unpacked, 0, (int)pent.UnpackedSize, entry.Name);
}
case Compression.Deflate:
default:
case Compression.DeflateOrNone:
return new ZLibStream (input, CompressionMode.Decompress);
case Compression.Zstd:
case Compression.ZstdOrNone:
{
var unpacked = ZstdDecompress (input, pent.UnpackedSize);
return new BinMemoryStream (unpacked, entry.Name);
}
default:
return input;
}
}
@@ -199,5 +231,15 @@ namespace GameRes.Formats.NeXAS
var decoder = new HuffmanDecoder (packed, dst);
return decoder.Unpack();
}
static private byte[] ZstdDecompress (Stream s, uint unpackedSize)
{
using (var ds = new ZstdNet.DecompressionStream (s))
{
var dst = new byte[unpackedSize];
ds.Read (dst, 0, dst.Length);
return dst;
}
}
}
}

View File

@@ -0,0 +1,50 @@
using GameRes.Utility;
using System.Collections.Generic;
using System.ComponentModel.Composition;
namespace GameRes.Formats.NipponIchi
{
[Export(typeof(ArchiveFormat))]
public class CASNOpener : ArchiveFormat
{
public override string Tag { get { return "DAT/CASN ShinHayarigami"; } }
public override string Description { get { return "Nippon Ichi Shin Hayarigami PS3 resource archive"; } }
public override uint Signature { get { return 0x4E534143; } } // 'CASN'
public override bool IsHierarchic { get { return false; } }
public override bool CanWrite { get { return false; } }
public override ArcFile TryOpen(ArcView file)
{
int count = Binary.BigEndian(file.View.ReadUInt16(6));
if (!IsSaneCount(count))
return null;
long index_offset = count * 10 + 30; //actually count*10 + 28, ignored two bytes of 000C or 000B
var dir = new List<Entry>(count);
for (int i = 0; i < count; ++i)
{
uint fstart = Binary.BigEndian(file.View.ReadUInt32(index_offset));
uint flength = Binary.BigEndian(file.View.ReadUInt32(index_offset + 4));
index_offset += 8;
byte c;
List<byte> namebyte = new List<byte>();
while (true)
{
c = file.View.ReadByte(index_offset);
if (c == 0) break;
namebyte.Add(c);
index_offset++;
}
index_offset += 3;
//var sjis = System.Text.Encoding.GetEncoding("Shift-JIS");
var name = System.Text.Encoding.ASCII.GetString(namebyte.ToArray());
var entry = Create<Entry>(name);
entry.Offset = fstart;
entry.Size = flength;
if (!entry.CheckPlacement(file.MaxOffset))
return null;
dir.Add(entry);
}
return new ArcFile(file, this, dir);
}
}
}

View File

@@ -0,0 +1,42 @@
using GameRes.Utility;
using System.Collections.Generic;
using System.ComponentModel.Composition;
namespace GameRes.Formats.NipponIchi
{
[Export(typeof(ArchiveFormat))]
public class PSFSOpener : ArchiveFormat
{
public override string Tag { get { return "DAT/PS_FS ShinHayarigami2"; } }
public override string Description { get { return "Nippon Ichi Shin Hayarigami2 PS3 resource archive"; } }
public override uint Signature { get { return 0x465F5350; } } // 'PS_F' of 'PS_FS_V1'
public override bool IsHierarchic { get { return false; } }
public override bool CanWrite { get { return false; } }
public override ArcFile TryOpen(ArcView file)
{
int count = Binary.BigEndian(file.View.ReadInt32(8));
if (!IsSaneCount(count))
return null;
long index_offset = 0x10;
var dir = new List<Entry>(count);
for (int i = 0; i < count; ++i)
{
string name = file.View.ReadString(index_offset, 0x30);
var entry = Create<Entry>(name);
entry.Offset = Binary.BigEndian(file.View.ReadUInt32(index_offset + 0x3C));
entry.Size = Binary.BigEndian(file.View.ReadUInt32(index_offset + 0x34));
index_offset += 0x40;
if (!entry.CheckPlacement(file.MaxOffset))
return null;
if (0 == file.View.ReadByte(entry.Offset))
{
entry.Offset += 0x10;
entry.Size -= 0x10;
}
dir.Add(entry);
}
return new ArcFile(file, this, dir);
}
}
}

View File

@@ -0,0 +1,39 @@
using System;
using System.ComponentModel.Composition;
using System.IO;
using System.Windows.Media;
namespace GameRes.Formats.NipponIchi
{
[Export(typeof(ImageFormat))]
public class NmtFormat : ImageFormat {
public override string Tag { get { return "NMT"; } }
public override string Description { get { return "NIS Multy Texform image format"; } }
public override uint Signature { get { return 0x6D73696E; } } // 'nism'
public override ImageMetaData ReadMetaData(IBinaryStream file)
{
var header = file.ReadHeader(0x30);
var signature = file.ReadHeader(15);
if (System.Text.Encoding.ASCII.GetString(signature.ToArray()) != "nismultitexform")
return null;
return new ImageMetaData
{
Width = header.ToUInt16(0x26),
Height = header.ToUInt16(0x28)
};
}
public override ImageData Read(IBinaryStream file, ImageMetaData info)
{
//uint imagedatasize = info.Width * info.Height * 4;
if (info == null) throw new NotSupportedException(string.Format("Not NMT texture format."));
file.Position = 0x30;
var data = file.ReadBytes(info.iWidth * info.iHeight * 4);
return ImageData.Create(info, PixelFormats.Bgra32, null, data);
}
public override void Write(Stream file, ImageData image)
{
throw new System.NotImplementedException("NmtFormat.Write not implemented");
}
}
}

View File

@@ -12,7 +12,7 @@ namespace GameRes.Formats.Properties {
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "17.0.3.0")]
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "17.10.0.0")]
public sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase {
private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings())));
@@ -813,5 +813,29 @@ namespace GameRes.Formats.Properties {
this["AFAEncodingCP"] = value;
}
}
[global::System.Configuration.UserScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Configuration.DefaultSettingValueAttribute("DXARC")]
public string DXAPassword {
get {
return ((string)(this["DXAPassword"]));
}
set {
this["DXAPassword"] = value;
}
}
[global::System.Configuration.UserScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Configuration.DefaultSettingValueAttribute("932")]
public int NexasEncodingCP {
get {
return ((int)(this["NexasEncodingCP"]));
}
set {
this["NexasEncodingCP"] = value;
}
}
}
}

View File

@@ -200,5 +200,11 @@
<Setting Name="AFAEncodingCP" Type="System.Int32" Scope="User">
<Value Profile="(Default)">932</Value>
</Setting>
<Setting Name="DXAPassword" Type="System.String" Scope="User">
<Value Profile="(Default)">DXARC</Value>
</Setting>
<Setting Name="NexasEncodingCP" Type="System.Int32" Scope="User">
<Value Profile="(Default)">932</Value>
</Setting>
</Settings>
</SettingsFile>

View File

Binary file not shown.

View File

@@ -54,7 +54,8 @@ namespace GameRes.Formats.Seraphim
public override ImageMetaData ReadMetaData (IBinaryStream stream)
{
var header = stream.ReadHeader (0x10);
if ('C' != header[0] || 'F' != header[1] || 0 != header[3])
uint sig = header.ToUInt16(0);
if (sig != Signature)
return null;
int packed_size = header.ToInt32 (12);
if (packed_size <= 0 || packed_size > stream.Length-0x10)

View File

@@ -41,7 +41,7 @@ namespace GameRes.Formats.Valkyria
internal interface IMg2Scheme
{
Mg2EncryptedStream CreateStream (Stream main, int offset, int length);
Mg2EncryptedStream CreateStream (Stream main, int offset, int length, int key);
ImageData CreateImage (BitmapSource bitmap, ImageMetaData info);
}
@@ -52,17 +52,17 @@ namespace GameRes.Formats.Valkyria
public override string Description { get { return "Valkyria image format"; } }
public override uint Signature { get { return 0x4F43494D; } } // 'MICO'
static readonly IMg2Scheme[] KnownSchemes = { new Mg2SchemeV1(), new Mg2SchemeV2() };
static readonly IMg2Scheme[] KnownSchemes = { new Mg2SchemeV1(), new Mg2SchemeV2(), new Mg2SchemeV3() };
public override ImageMetaData ReadMetaData (IBinaryStream file)
{
var header = file.ReadHeader (0x10);
if (!header.AsciiEqual (4, "CG01"))
if (!header.AsciiEqual (4, "CG01") && !header.AsciiEqual (4, "CG02"))
return null;
int length = header.ToInt32 (8);
foreach (var scheme in KnownSchemes)
{
using (var input = scheme.CreateStream (file.AsStream, 0x10, length))
using (var input = scheme.CreateStream (file.AsStream, 0x10, length, length))
using (var img = new BinaryStream (input, file.Name))
{
ImageFormat format;
@@ -102,7 +102,7 @@ namespace GameRes.Formats.Valkyria
BitmapSource ReadBitmapSource (Stream file, Mg2MetaData meta)
{
BitmapSource frame;
using (var input = meta.Scheme.CreateStream (file, 0x10, meta.ImageLength))
using (var input = meta.Scheme.CreateStream (file, 0x10, meta.ImageLength, meta.ImageLength))
using (var img = new BinaryStream (input, meta.FileName))
{
var image = meta.Format.Read (img, meta);
@@ -116,7 +116,7 @@ namespace GameRes.Formats.Valkyria
var pixels = new byte[stride * (int)meta.Height];
frame.CopyPixels (pixels, stride, 0);
using (var input = meta.Scheme.CreateStream (file, 0x10+meta.ImageLength, meta.AlphaLength))
using (var input = meta.Scheme.CreateStream (file, 0x10+meta.ImageLength, meta.AlphaLength, meta.ImageLength))
{
var decoder = BitmapDecoder.Create (input, BitmapCreateOptions.None, BitmapCacheOption.OnLoad);
BitmapSource alpha_frame = decoder.Frames[0];
@@ -148,32 +148,53 @@ namespace GameRes.Formats.Valkyria
internal class Mg2EncryptedStream : StreamRegion
{
readonly byte m_version;
readonly int m_threshold;
readonly byte m_key;
readonly byte m_key0;
readonly byte m_key1;
protected Mg2EncryptedStream (Stream main, int offset, int length, int threshold, byte key)
protected Mg2EncryptedStream (Stream main, int offset, int length, byte version, int threshold, byte key0, byte key1)
: base (main, offset, length, true)
{
m_version = version;
m_threshold = threshold;
m_key = key;
m_key0 = key0;
m_key1 = key1;
}
public static Mg2EncryptedStream CreateV1 (Stream main, int offset, int length)
{
return new Mg2EncryptedStream (main, offset, length, length / 5, 0);
return new Mg2EncryptedStream(main, offset, length, 1, length / 5, 0, 0);
}
public static Mg2EncryptedStream CreateV2 (Stream main, int offset, int length)
{
return new Mg2EncryptedStream (main, offset, length, Math.Min (25, length), (byte)length);
return new Mg2EncryptedStream( main, offset, length, 2, Math.Min(25, length), (byte)length, 0);
}
public static Mg2EncryptedStream CreateV3 (Stream main, int offset, int length, int key)
{
return new Mg2EncryptedStream(main, offset, length, 3, 0, (byte)(key >> 1), (byte)((key & 1) + (key >> 3)));
}
public override int Read (byte[] buffer, int offset, int count)
{
int pos = (int)Position;
int read = base.Read (buffer, offset, count);
for (int i = 0; i < read && pos < m_threshold; ++i)
buffer[offset+i] ^= (byte)(m_key + pos++);
long pos = Position;
int read = base.Read(buffer, offset, count);
if (m_version == 3)
{
for (int i = 0; i < read; ++i)
{
buffer[offset+i] ^= (byte)((pos >> 4) ^ (pos + m_key0) ^ m_key1);
pos++;
}
}
else
{
for (int i = 0; i < read && pos < m_threshold; ++i)
buffer[offset+i] ^= (byte)(m_key0 + pos++);
}
return read;
}
@@ -181,15 +202,24 @@ namespace GameRes.Formats.Valkyria
{
long pos = Position;
int b = base.ReadByte();
if (b != -1 && pos < m_threshold)
b ^= (byte)(m_key + pos);
if (m_version == 3)
{
b ^= (byte)((pos >> 4) ^ (pos + m_key0) ^ m_key1);
}
else
{
if (b != -1 && pos < m_threshold)
b ^= (byte)(m_key0 + pos);
}
return b;
}
}
internal class Mg2SchemeV1 : IMg2Scheme
{
public Mg2EncryptedStream CreateStream (Stream main, int offset, int length)
public Mg2EncryptedStream CreateStream (Stream main, int offset, int length, int key)
{
return Mg2EncryptedStream.CreateV1 (main, offset, length);
}
@@ -203,7 +233,7 @@ namespace GameRes.Formats.Valkyria
internal class Mg2SchemeV2 : IMg2Scheme
{
public Mg2EncryptedStream CreateStream (Stream main, int offset, int length)
public Mg2EncryptedStream CreateStream (Stream main, int offset, int length, int key)
{
return Mg2EncryptedStream.CreateV2 (main, offset, length);
}
@@ -215,4 +245,19 @@ namespace GameRes.Formats.Valkyria
return new ImageData (frame, info);
}
}
internal class Mg2SchemeV3 : IMg2Scheme
{
public Mg2EncryptedStream CreateStream (Stream main, int offset, int length, int key)
{
return Mg2EncryptedStream.CreateV3 (main, offset, length, key);
}
public ImageData CreateImage (BitmapSource frame, ImageMetaData info)
{
frame = new TransformedBitmap(frame, new ScaleTransform { ScaleY = -1 });
frame.Freeze();
return new ImageData(frame, info);
}
}
}

View File

@@ -70,7 +70,7 @@ namespace GameRes.Formats.YaneSDK
entry.EncryptedSize = index.ReadUInt16();
entry.Size = index.ReadUInt32();
entry.Offset = index.ReadUInt32();
if (!entry.CheckPlacement (file.MaxOffset) || entry.Offset <= data_offset)
if (!entry.CheckPlacement (file.MaxOffset) || entry.Offset < data_offset)
return null;
dir.Add (entry);
}

View File

@@ -202,6 +202,12 @@
<setting name="AFAEncodingCP" serializeAs="String">
<value>932</value>
</setting>
<setting name="DXAPassword" serializeAs="String">
<value>DXARC</value>
</setting>
<setting name="NexasEncodingCP" serializeAs="String">
<value>932</value>
</setting>
</GameRes.Formats.Properties.Settings>
</userSettings>
<startup><supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.6.1" /></startup>

View File

@@ -6,8 +6,10 @@
<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.Numerics.Vectors" version="4.5.0" 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>
<package id="ZstdNet" version="1.4.5" targetFramework="net46" />
</packages>

View File

@@ -100,6 +100,10 @@
</BootstrapperPackage>
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<PropertyGroup>
<PostBuildEvent>
</PostBuildEvent>
</PropertyGroup>
<!-- 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.
<Target Name="BeforeBuild">

View File

@@ -29,8 +29,6 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Image.Convert", "Image.Conv
{A8865685-27CC-427B-AC38-E48D2AD05DF4} = {A8865685-27CC-427B-AC38-E48D2AD05DF4}
EndProjectSection
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SchemeBuilder", "SchemeBuilder\SchemeBuilder.csproj", "{B7E7EBFB-C06E-4FC8-9AF2-7CD132AB15FD}"
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}"
@@ -76,10 +74,6 @@ Global
{757EB8B1-F62C-4690-AC3D-DAE4A5576B3E}.Prerelease|Any CPU.Build.0 = Prerelease|Any CPU
{757EB8B1-F62C-4690-AC3D-DAE4A5576B3E}.Release|Any CPU.ActiveCfg = Release|Any CPU
{757EB8B1-F62C-4690-AC3D-DAE4A5576B3E}.Release|Any CPU.Build.0 = Release|Any CPU
{B7E7EBFB-C06E-4FC8-9AF2-7CD132AB15FD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{B7E7EBFB-C06E-4FC8-9AF2-7CD132AB15FD}.Prerelease|Any CPU.ActiveCfg = Prerelease|Any CPU
{B7E7EBFB-C06E-4FC8-9AF2-7CD132AB15FD}.Release|Any CPU.ActiveCfg = Release|Any CPU
{B7E7EBFB-C06E-4FC8-9AF2-7CD132AB15FD}.Release|Any CPU.Build.0 = Release|Any CPU
{60054FD9-4472-4BB4-9E3D-2F80D3D22468}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{60054FD9-4472-4BB4-9E3D-2F80D3D22468}.Debug|Any CPU.Build.0 = Debug|Any CPU
{60054FD9-4472-4BB4-9E3D-2F80D3D22468}.Prerelease|Any CPU.ActiveCfg = Prerelease|Any CPU

View File

@@ -33,7 +33,7 @@ namespace GameRes
{
public static class Encodings
{
public static readonly Encoding cp932 = Encoding.GetEncoding (932);
public static readonly Encoding cp932 = Encoding.GetEncoding(932);
public static Encoding WithFatalFallback (this Encoding enc)
{

View File

@@ -170,6 +170,27 @@ namespace GameRes
return bin;
}
internal int FindEoS (int start, int length, Encoding enc)
{
int eos_pos = -1;
if (enc.IsUtf16())
{
for (int i = start+1; i < start+length; i += 2)
{
if (m_buffer[i-1] == 0 && m_buffer[i] == 0)
{
eos_pos = i - 1;
break;
}
}
}
else
{
eos_pos = Array.IndexOf<byte> (m_buffer, 0, start, length);
}
return eos_pos;
}
uint ReadSignature ()
{
if (m_header_size >= 4)
@@ -307,10 +328,7 @@ namespace GameRes
public string ReadCString (int length, Encoding enc)
{
length = FillBuffer (length);
int i;
for (i = 0; i < length; ++i)
if (0 == m_buffer[m_buffer_pos+i])
break;
int i = FindEoS(m_buffer_pos, length, enc) - m_buffer_pos;
string s = enc.GetString (m_buffer, m_buffer_pos, i);
m_buffer_pos += length;
return s;

View File

@@ -65,3 +65,10 @@ Korean translation by [mireado](https://github.com/mireado), [overworks](https:/
Simplified Chinese translation by [elasticblitz](https://github.com/elasticblitz), [PeratX](https://github.com/PeratX) and [taroxd](https://github.com/taroxd)
Japanese translation by [haniwa55](https://github.com/haniwa55)
Contributors
------
<a href="https://github.com/crskycode/GARbro/graphs/contributors">
<img src="https://contrib.rocks/image?repo=crskycode/GARbro" />
</a>