pkNX/pkNX.Containers/GARC/GARC.cs
Kurt 2578ba073c Refactoring
File scoped namespaces for all lib projects
netstandard2.0 => net6; now uniform. bye netframework!
2022-10-01 17:08:17 -07:00

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;
}
}