Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions ArcFormats/ArcFormats.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -265,6 +265,7 @@
<Compile Include="MAGES\ImageBIN.cs" />
<Compile Include="Mugi\ArcBIN.cs" />
<Compile Include="Musica\ArcPAK.cs" />
<Compile Include="NekoNovel\ArcNKPACK.cs" />
<Compile Include="NEKOWORKs\ArcPACK.cs" />
<Compile Include="NipponIchi\ArcCASN.cs" />
<Compile Include="NipponIchi\ArcPSFS.cs" />
Expand Down
94 changes: 94 additions & 0 deletions ArcFormats/NekoNovel/ArcNKPACK.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
using System;
using System.IO;
using System.Collections.Generic;
using System.Text;
using GameRes.Compression;
using System.ComponentModel.Composition;
using CommunityToolkit.HighPerformance;

namespace GameRes.Formats.NekoNovel
{
[Export(typeof(ArchiveFormat))]
public class NkpackOpener : ArchiveFormat
{
public override string Tag => "NKPACK/NekoNovel";
public override string Description => "NekoNovel resource archive";
public override uint Signature => 0u;
public override bool IsHierarchic => true;
public override bool CanWrite => false;

public NkpackOpener()
{
Extensions = new string[] { "nkpack" };
}

private static readonly byte[] s_mHeader = new byte[]
{
0x0F, 0x00, 0x00, 0x00, 0x4E, 0x4B, 0x4E, 0x4F, 0x45, 0x56, 0x4C, 0x20, 0x50, 0x41, 0x43, 0x4B,
0x41, 0x47, 0x45
};

public override ArcFile TryOpen(ArcView file)
{
if (!file.View.BytesEqual(0L, s_mHeader))
{
return null;
}
using (ArcViewStream stream = file.CreateStream())
{
stream.Seek(-4L, SeekOrigin.End);
uint entryOffset = ~stream.ReadUInt32();

stream.Seek(entryOffset, SeekOrigin.Begin);
string signature = ReadString(stream);

int count = stream.ReadInt32();
List<Entry> entries = new List<Entry>(count);
for (int i = 0; i < count; ++i)
{
string name = ReadString(stream);

PackedEntry entry = Create<PackedEntry>(name);
entry.Offset = ~stream.ReadUInt32();
entry.UnpackedSize = stream.ReadUInt32();
entry.Size = ~stream.ReadUInt32();
entry.IsPacked = true;

if (!entry.CheckPlacement(file.MaxOffset))
{
return null;
}

entries.Add(entry);
}

return new ArcFile(file, this, entries);
}
}

public override Stream OpenEntry(ArcFile arc, Entry entry)
{
if (!(entry is PackedEntry e))
{
return arc.File.CreateStream(entry.Offset, entry.Size);
}
return new ZLibStream(arc.File.CreateStream(e.Offset, e.Size), CompressionMode.Decompress);
}

private unsafe static string ReadString(Stream stream)
{
uint length = 0u;
stream.Read(new Span<byte>(&length, 4));

if (length == 0u)
{
return string.Empty;
}

byte[] data = new byte[length];
stream.Read(data, 0, data.Length);

return Encoding.UTF8.GetString(data);
}
}
}
Loading