mirror of
https://github.com/kwsch/pkNX.git
synced 2026-04-20 16:47:50 -05:00
File scoped namespaces for all lib projects netstandard2.0 => net6; now uniform. bye netframework!
135 lines
3.9 KiB
C#
135 lines
3.9 KiB
C#
using System.Collections.Generic;
|
|
using System.IO;
|
|
using System.Linq;
|
|
using System.Threading;
|
|
using System.Threading.Tasks;
|
|
|
|
namespace pkNX.Containers;
|
|
|
|
public class GARC : LargeContainer
|
|
{
|
|
private GARCHeader Header;
|
|
private FATO FATO;
|
|
private FATB FATB;
|
|
|
|
#pragma warning disable CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable.
|
|
public GARC(BinaryReader br) => OpenRead(br);
|
|
public GARC(string file) => OpenBinary(file);
|
|
#pragma warning restore CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable.
|
|
|
|
public GARC(IReadOnlyList<string> files, GARCVersion version = GARCVersion.VER_6)
|
|
{
|
|
Header = new GARCHeader(version);
|
|
FATO = new FATO(files.Count);
|
|
FATB = new FATB(files);
|
|
Files = new byte[]?[files.Count];
|
|
}
|
|
|
|
protected override void Initialize()
|
|
{
|
|
Reader!.BaseStream.Position = 0;
|
|
Header = new GARCHeader(Reader);
|
|
FATO = new FATO(Reader);
|
|
FATB = new FATB(Reader, Header.DataOffset);
|
|
Files = new byte[]?[FATO.EntryCount];
|
|
}
|
|
|
|
protected override int GetFileOffset(int file, int subFile = 0)
|
|
{
|
|
var f = FATB[file].SubEntries[subFile];
|
|
return f.Start;
|
|
}
|
|
|
|
public override byte[] GetEntry(int index, int subFile)
|
|
{
|
|
var f = FATB[index].SubEntries[subFile];
|
|
if (f.File is byte[] data)
|
|
return data;
|
|
|
|
data = f.GetFileData(Reader!.BaseStream);
|
|
f.File = data; // cache for future fetches
|
|
return data;
|
|
}
|
|
|
|
public override void SetEntry(int index, byte[]? value, int subFile)
|
|
{
|
|
Modified |= value != null && !GetEntry(index, subFile).SequenceEqual(value);
|
|
var f = FATB[index].SubEntries[subFile];
|
|
f.File = value;
|
|
}
|
|
|
|
protected override Task Pack(BinaryWriter bw, ContainerHandler handler, CancellationToken token)
|
|
{
|
|
return Task.Run(() => PackGARC(bw, handler, token), token);
|
|
}
|
|
|
|
private void PackGARC(BinaryWriter bw, ContainerHandler handler, CancellationToken token)
|
|
{
|
|
StartPack();
|
|
handler.Initialize(Count);
|
|
|
|
WriteIntro(bw);
|
|
int dataOffset = (int)bw.BaseStream.Position;
|
|
for (int i = 0; i < Count; i++)
|
|
{
|
|
if (token.IsCancellationRequested)
|
|
return;
|
|
WriteEntry(bw, i, dataOffset);
|
|
}
|
|
WriteIntro(bw, true);
|
|
bw.Flush();
|
|
}
|
|
|
|
private void StartPack()
|
|
{
|
|
Header.ContentLargestUnpadded = 0;
|
|
Header.ContentLargestPadded = 0;
|
|
}
|
|
|
|
private void WriteIntro(BinaryWriter bw, bool lastPass = false)
|
|
{
|
|
Header.Write(bw);
|
|
FATO.Write(bw);
|
|
FATB.Write(bw);
|
|
if (lastPass)
|
|
Header.DataOffset = (int)bw.BaseStream.Position;
|
|
}
|
|
|
|
private void WriteEntry(BinaryWriter bw, int i, int DataOffset)
|
|
{
|
|
FATO[i].Offset = (uint)bw.BaseStream.Position;
|
|
FATB[i].WriteEntries(bw, Reader!.BaseStream, Header.ContentPadToNearest, DataOffset,
|
|
ref Header.ContentLargestUnpadded, ref Header.ContentLargestPadded);
|
|
}
|
|
|
|
public override void Dump(string path, ContainerHandler handler)
|
|
{
|
|
string format = this.GetFileFormatString();
|
|
|
|
if (!Directory.Exists(path))
|
|
Directory.CreateDirectory(path);
|
|
|
|
handler.Initialize(Count);
|
|
for (int i = 0; i < Count; i++)
|
|
{
|
|
FATB[i].Dump(path, i, format, Reader!.BaseStream, Header.DataOffset);
|
|
handler.StepFile(i+1);
|
|
}
|
|
}
|
|
|
|
public static GARC? GetGARC(BinaryReader br)
|
|
{
|
|
if (br.BaseStream.Length < 20)
|
|
return null;
|
|
br.BaseStream.Position = 0;
|
|
if (br.ReadUInt32() != GARCHeader.MAGIC)
|
|
return null;
|
|
br.BaseStream.Position = 10;
|
|
var ver = br.ReadUInt16();
|
|
|
|
if (ver is 0x0600 or 0x0400)
|
|
return new GARC(br);
|
|
return null;
|
|
}
|
|
}
|