1
0
mirror of synced 2024-11-12 02:00:50 +01:00

Fixed multiple issues with NARC library (#511)

* Fixed multiple issues with NARC library

- Support for NARC files with folders (read-only)
- Fixed alignment of files.
- Optimised loading.

* Quick fixes

* Cleanup
This commit is contained in:
JkKU 2022-09-10 20:06:12 +02:00 committed by GitHub
parent 38bd5f6bca
commit c928879082
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

View File

@ -2,6 +2,7 @@ using Syroot.BinaryData;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
using System.Linq;
using System.Text; using System.Text;
using Toolbox.Library; using Toolbox.Library;
using Toolbox.Library.IO; using Toolbox.Library.IO;
@ -28,9 +29,7 @@ namespace FirstPlugin {
// private // private
private Header header; private Header header;
private BTAF btaf; private byte[] bfntUnk = new byte[] { 0x00, 0x00, 0x01, 0x00 };
private BTNF btnf;
private GMIF gmif;
private List<FileEntry> files = new List<FileEntry>(); private List<FileEntry> files = new List<FileEntry>();
@ -56,35 +55,88 @@ namespace FirstPlugin {
public void Load(Stream stream) { public void Load(Stream stream) {
using(FileReader reader = new FileReader(stream)) { using(FileReader reader = new FileReader(stream)) {
//DirectoryEntry currentDirEntry = rootDir;
string currentDir = string.Empty;
header = new Header(reader); header = new Header(reader);
for(uint i = 0; i < header.DataBlocks; i++) { reader.ReadSignature(4, "BTAF");
long PositionBuf = reader.Position;
string cSectionMagic = Encoding.ASCII.GetString(reader.ReadBytes(4)).ToUpperInvariant();
uint cSectionSize = reader.ReadUInt32();
switch(cSectionMagic) { // The positions where the sections' reading was left last time.
case "BTAF": long bfatIndex = reader.Position + 8;
btaf = new BTAF(reader); long bfntIndex = reader.Position + reader.ReadUInt32(); // From the BFAT length.
break; long fimgIndex;
case "BTNF":
btnf = new BTNF(reader, (uint) btaf.FileDataArray.Length); uint fileCount = reader.ReadUInt32();
break;
case "GMIF": uint currentFileOffset;
gmif = new GMIF(reader, btaf); uint currentFileEnd;
break;
reader.Position = bfntIndex;
fimgIndex = reader.Position + reader.ReadUInt32() + 4; // Sets FIMG section begining.
bfntUnk = reader.ReadBytes(reader.ReadInt32() - 4);
for(int i = 0; i < fileCount; i++) {
byte nameLength = reader.ReadByte();
if(nameLength == 0x00) { // End of the "folder".
List<int> slashIndices = new List<int>();
for(int j = 0; j < currentDir.Length; j++)
if(currentDir[j] == '/')
slashIndices.Add(j);
int lastSlash = slashIndices.Last();
slashIndices.Remove(lastSlash);
int slashBeforeLast = slashIndices.Count > 0 ? slashIndices.Last() : 0;
currentDir = currentDir.Remove(slashBeforeLast + 1);
if(currentDir.Length == 1)
currentDir = string.Empty;
i--;
continue;
} }
reader.Position = PositionBuf + cSectionSize; if(nameLength >= 0x80) { // If it is a "folder".
// Disable file modifications:
CanSave = false;
CanAddFiles = false;
CanRenameFiles = false;
CanReplaceFiles = false;
CanDeleteFiles = false;
string dirName = reader.ReadString(nameLength & 0x7F);
currentDir += dirName + "/";
reader.Position += 2;
i--;
continue;
} }
for(int i = 0; i < btnf.FileNames.Length; i++) // Read BFAT section:
using(reader.TemporarySeek()) {
reader.Position = bfatIndex;
currentFileOffset = reader.ReadUInt32();
currentFileEnd = reader.ReadUInt32();
bfatIndex = reader.Position;
}
string name = reader.ReadString(nameLength);
using(reader.TemporarySeek()) {
reader.Position = fimgIndex + currentFileOffset;
files.Add(new FileEntry(this) { files.Add(new FileEntry(this) {
FileName = btnf.FileNames[i], FileName = currentDir + name,
BlockData = gmif.FilesData[i] BlockData = reader.ReadBytes((int) (currentFileEnd - currentFileOffset))
}); });
} }
} }
}
}
public class FileEntry : ArchiveFileInfo public class FileEntry : ArchiveFileInfo
{ {
@ -219,7 +271,7 @@ namespace FirstPlugin {
writer.Write(header.Version); // Version. writer.Write(header.Version); // Version.
long headerLengthPorsition = writer.Position; long headerLengthPorsition = writer.Position;
writer.Write(0x00000000); // Skips length writing. writer.Position += 4; // Skips length writing.
writer.Write(header.HeaderSize); // Header length. writer.Write(header.HeaderSize); // Header length.
writer.Write(header.DataBlocks); // Section count. writer.Write(header.DataBlocks); // Section count.
@ -230,21 +282,21 @@ namespace FirstPlugin {
writer.Write((uint) files.Count); // File count. writer.Write((uint) files.Count); // File count.
for(uint i = 0; i < files.Count; i++) // Reads unset bytes per file. (Reserved space for later) for(uint i = 0; i < files.Count; i++) // Reads unset bytes per file. (Reserved space for later)
writer.Write((long) 0x0000000000000000); writer.Position += 8;
WriteSectionLength(btafPosition); WriteSectionLength(btafPosition);
#endregion #endregion
#region BTNF #region BTNF
long btnfPosition = WriteSectionHeader("BTNF"); // Header. long btnfPosition = WriteSectionHeader("BTNF"); // Header.
writer.Write(btnf.Unknown); writer.Write(bfntUnk.Length + 4);
writer.Write(bfntUnk);
foreach(ArchiveFileInfo file in files) foreach(ArchiveFileInfo file in files)
writer.Write(file.FileName, BinaryStringFormat.ByteLengthPrefix); writer.Write(file.FileName, BinaryStringFormat.ByteLengthPrefix);
writer.Write((byte) 0x00); writer.Write((byte) 0x00);
writer.Align(32); writer.Align(128);
writer.Position += 8;
WriteSectionLength(btnfPosition); WriteSectionLength(btnfPosition);
#endregion #endregion
@ -257,7 +309,7 @@ namespace FirstPlugin {
WriteBTAFEntry(); // BTAF offset WriteBTAFEntry(); // BTAF offset
writer.Write(file.FileData); writer.Write(file.FileData);
WriteBTAFEntry(); // BTAF size. WriteBTAFEntry(); // BTAF size.
writer.Align(16); writer.Align(128);
} }
WriteSectionLength(gmifPosition); WriteSectionLength(gmifPosition);
@ -270,7 +322,7 @@ namespace FirstPlugin {
long startPosition = writer.Position; long startPosition = writer.Position;
writer.Write(magic, BinaryStringFormat.NoPrefixOrTermination, Encoding.ASCII); // Magic. writer.Write(magic, BinaryStringFormat.NoPrefixOrTermination, Encoding.ASCII); // Magic.
writer.Write(0x00000000); // Skips length position. writer.Position += 4; // Skips length position.
return startPosition; return startPosition;
} }
@ -296,60 +348,5 @@ namespace FirstPlugin {
} }
} }
} }
#region Sections
internal class BTAF {
public FD[] FileDataArray;
public BTAF() { }
public BTAF(BinaryDataReader reader) {
uint numberOfFiles = reader.ReadUInt32();
FileDataArray = new FD[numberOfFiles];
for(ulong i = 0; i < numberOfFiles; i++) {
FileDataArray[i].offset = reader.ReadUInt32();
FileDataArray[i].size = reader.ReadUInt32();
}
}
public class FD
{
public uint offset;
public uint size;
}
}
internal class BTNF {
public string[] FileNames;
public ulong Unknown;
public BTNF() { }
public BTNF(BinaryDataReader reader, uint numberOfFiles) {
Unknown = reader.ReadUInt64();
FileNames = new string[numberOfFiles];
for(int i = 0; i < numberOfFiles; i++) {
FileNames[i] = reader.ReadString(BinaryStringFormat.ByteLengthPrefix, Encoding.ASCII);
}
}
}
internal class GMIF {
public byte[][] FilesData;
public GMIF() { }
public GMIF(BinaryDataReader reader, BTAF btaf) {
FilesData = new byte[btaf.FileDataArray.Length][];
long PositionBuf = reader.Position;
for(int i = 0; i < btaf.FileDataArray.Length; i++) {
reader.Position = PositionBuf + btaf.FileDataArray[i].offset;
FilesData[i] = reader.ReadBytes(
(int) (btaf.FileDataArray[i].size - btaf.FileDataArray[i].offset));
}
}
}
#endregion
} }
} }