mirror of
https://github.com/XorTroll/EveryFileExplorer.git
synced 2024-11-23 09:59:41 +00:00
300 lines
8.8 KiB
C#
300 lines
8.8 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using System.Text;
|
|
using System.IO;
|
|
using NDS.Nitro;
|
|
using LibEveryFileExplorer.Files.SimpleFileSystem;
|
|
using LibEveryFileExplorer.Files;
|
|
using System.Drawing;
|
|
using System.Windows.Forms;
|
|
using NDS.UI;
|
|
using LibEveryFileExplorer.IO;
|
|
|
|
namespace NDS
|
|
{
|
|
public class UtilityBin : FileFormat<UtilityBin.UtilityBinIdentifier>, IViewable, IWriteable
|
|
{
|
|
public UtilityBin(byte[] Data)
|
|
{
|
|
EndianBinaryReader er = new EndianBinaryReader(new MemoryStream(Data), Endianness.LittleEndian);
|
|
Header = new UtilityBinHeader(er);
|
|
|
|
er.BaseStream.Position = Header.FileNameTableOffset;
|
|
FileNameTable = new UtilityBinFNT(er);
|
|
|
|
er.BaseStream.Position = Header.FileAllocationTableOffset;
|
|
FileAllocationTable = new FileAllocationEntry[Header.FileAllocationTableSize / 8];
|
|
for (int i = 0; i < Header.FileAllocationTableSize / 8; i++)
|
|
{
|
|
FileAllocationTable[i] = new FileAllocationEntry(er);
|
|
}
|
|
|
|
FileData = new byte[Header.FileAllocationTableSize / 8][];
|
|
for (int i = 0; i < Header.FileAllocationTableSize / 8; i++)
|
|
{
|
|
er.BaseStream.Position = FileAllocationTable[i].fileTop;
|
|
FileData[i] = er.ReadBytes((int)FileAllocationTable[i].fileSize);
|
|
}
|
|
er.Close();
|
|
}
|
|
|
|
public Form GetDialog()
|
|
{
|
|
return new UtilityBinViewer(this);
|
|
}
|
|
|
|
public string GetSaveDefaultFileFilter()
|
|
{
|
|
return "NDS Wifi Archive (utility.bin)|utility.bin";
|
|
}
|
|
|
|
public byte[] Write()
|
|
{
|
|
MemoryStream m = new MemoryStream();
|
|
EndianBinaryWriter er = new EndianBinaryWriter(m, Endianness.LittleEndian);
|
|
er.Write((uint)0);
|
|
er.Write((uint)0);
|
|
|
|
er.Write((uint)0);
|
|
er.Write((uint)0);
|
|
|
|
long curpos = er.BaseStream.Position;
|
|
er.BaseStream.Position = 0;
|
|
er.Write((uint)curpos);
|
|
er.BaseStream.Position = curpos;
|
|
|
|
FileNameTable.Write(er);
|
|
while ((er.BaseStream.Position % 4) != 0) er.Write((byte)0xFF);
|
|
|
|
long curpos2 = er.BaseStream.Position;
|
|
er.BaseStream.Position = 4;
|
|
er.Write((uint)(curpos2 - curpos));
|
|
er.BaseStream.Position = curpos2;
|
|
|
|
while((er.BaseStream.Position % 0x20) != 0) er.Write((byte)0);
|
|
|
|
curpos = er.BaseStream.Position;
|
|
er.BaseStream.Position = 8;
|
|
er.Write((uint)curpos);
|
|
er.BaseStream.Position = curpos;
|
|
|
|
er.BaseStream.Position += FileAllocationTable.Length * 8;
|
|
|
|
curpos2 = er.BaseStream.Position;
|
|
er.BaseStream.Position = 12;
|
|
er.Write((uint)(curpos2 - curpos));
|
|
er.BaseStream.Position = curpos2;
|
|
|
|
while ((er.BaseStream.Position % 0x20) != 0) er.Write((byte)0);
|
|
|
|
for (int i = 0; i < FileData.Length; i++)
|
|
{
|
|
while ((er.BaseStream.Position % 4) != 0) er.Write((byte)0xFF);
|
|
FileAllocationTable[i].fileTop = (uint)er.BaseStream.Position;
|
|
FileAllocationTable[i].fileBottom = (uint)er.BaseStream.Position + (uint)FileData[i].Length;
|
|
er.Write(FileData[i], 0, FileData[i].Length);
|
|
}
|
|
|
|
while ((er.BaseStream.Position % 4) != 0) er.Write((byte)0xFF);
|
|
|
|
er.BaseStream.Position = curpos;
|
|
foreach (var v in FileAllocationTable) v.Write(er);
|
|
|
|
byte[] result = m.ToArray();
|
|
er.Close();
|
|
return result;
|
|
}
|
|
|
|
public UtilityBinHeader Header;
|
|
public class UtilityBinHeader
|
|
{
|
|
public UtilityBinHeader(EndianBinaryReader er)
|
|
{
|
|
FileNameTableOffset = er.ReadUInt32();
|
|
FileNameTableSize = er.ReadUInt32();
|
|
FileAllocationTableOffset = er.ReadUInt32();
|
|
FileAllocationTableSize = er.ReadUInt32();
|
|
}
|
|
public UInt32 FileNameTableOffset;
|
|
public UInt32 FileNameTableSize;
|
|
public UInt32 FileAllocationTableOffset;
|
|
public UInt32 FileAllocationTableSize;
|
|
}
|
|
public UtilityBinFNT FileNameTable;
|
|
public class UtilityBinFNT
|
|
{
|
|
public UtilityBinFNT(EndianBinaryReader er)
|
|
{
|
|
DirectoryTable = new List<DirectoryTableEntry>();
|
|
DirectoryTable.Add(new DirectoryTableEntry(er));
|
|
for (int i = 0; i < DirectoryTable[0].dirParentID - 1; i++)
|
|
{
|
|
DirectoryTable.Add(new DirectoryTableEntry(er));
|
|
}
|
|
EntryNameTable = new List<EntryNameTableEntry>();
|
|
int dirend = 0;
|
|
while (dirend < DirectoryTable[0].dirParentID)
|
|
{
|
|
byte entryNameLength = er.ReadByte();
|
|
er.BaseStream.Position--;
|
|
if (entryNameLength == 0)
|
|
{
|
|
EntryNameTable.Add(new EntryNameTableEndOfDirectoryEntry(er));
|
|
dirend++;
|
|
}
|
|
else if (entryNameLength < 0x80) EntryNameTable.Add(new EntryNameTableFileEntry(er));
|
|
else EntryNameTable.Add(new EntryNameTableDirectoryEntry(er));
|
|
}
|
|
}
|
|
public void Write(EndianBinaryWriter er)
|
|
{
|
|
foreach (DirectoryTableEntry e in DirectoryTable) e.Write(er);
|
|
foreach (EntryNameTableEntry e in EntryNameTable) e.Write(er);
|
|
}
|
|
public List<DirectoryTableEntry> DirectoryTable;
|
|
public List<EntryNameTableEntry> EntryNameTable;
|
|
}
|
|
public FileAllocationEntry[] FileAllocationTable;
|
|
public Byte[][] FileData;
|
|
|
|
public void FromFileSystem(SFSDirectory Root)
|
|
{
|
|
int did = 0;
|
|
int fid = 0;
|
|
Root.UpdateIDs(ref did, ref fid);
|
|
//FileNameTable.numFiles = (ushort)Root.TotalNrSubFiles;
|
|
//List<byte> Data = new List<byte>();
|
|
uint nrfiles = Root.TotalNrSubFiles;
|
|
FileAllocationTable = new FileAllocationEntry[nrfiles];
|
|
//FATB.allocationTable.Clear();
|
|
for (ushort i = 0; i < nrfiles; i++)
|
|
{
|
|
var f = Root.GetFileByID(i);
|
|
FileAllocationTable[i] = new FileAllocationEntry(0, 0);
|
|
//FATB.allocationTable.Add(new FileAllocationEntry((uint)Data.Count, (uint)f.Data.Length));
|
|
//Data.AddRange(f.Data);
|
|
//while ((Data.Count % 4) != 0) Data.Add(0xFF);
|
|
}
|
|
//FIMG.fileImage = Data.ToArray();
|
|
FileNameTable.DirectoryTable.Clear();
|
|
NitroFSUtil.GenerateDirectoryTable(FileNameTable.DirectoryTable, Root);
|
|
uint offset2 = FileNameTable.DirectoryTable[0].dirEntryStart;
|
|
ushort fileId = 0;
|
|
FileNameTable.EntryNameTable.Clear();
|
|
NitroFSUtil.GenerateEntryNameTable(FileNameTable.DirectoryTable, FileNameTable.EntryNameTable, Root, ref offset2, ref fileId);
|
|
}
|
|
|
|
public SFSDirectory ToFileSystem()
|
|
{
|
|
bool treereconstruct = false;//Some programs do not write the Directory Table well, so sometimes I need to reconstruct the tree based on the fnt, which is bad!
|
|
List<SFSDirectory> dirs = new List<SFSDirectory>();
|
|
dirs.Add(new SFSDirectory("/", true));
|
|
dirs[0].DirectoryID = 0xF000;
|
|
|
|
uint nrdirs = FileNameTable.DirectoryTable[0].dirParentID;
|
|
for (int i = 1; i < nrdirs; i++)
|
|
{
|
|
dirs.Add(new SFSDirectory((ushort)(0xF000 + i)));
|
|
}
|
|
for (int i = 1; i < nrdirs; i++)
|
|
{
|
|
if (FileNameTable.DirectoryTable[i].dirParentID - 0xF000 == i)
|
|
{
|
|
treereconstruct = true;
|
|
foreach (var v in dirs)
|
|
{
|
|
v.Parent = null;
|
|
}
|
|
break;
|
|
}
|
|
dirs[i].Parent = dirs[FileNameTable.DirectoryTable[i].dirParentID - 0xF000];
|
|
}
|
|
if (!treereconstruct)
|
|
{
|
|
for (int i = 0; i < nrdirs; i++)
|
|
{
|
|
for (int j = 0; j < nrdirs; j++)
|
|
{
|
|
if (dirs[i] == dirs[j].Parent)
|
|
{
|
|
dirs[i].SubDirectories.Add(dirs[j]);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
uint offset = nrdirs * 8;
|
|
ushort fileid = FileNameTable.DirectoryTable[0].dirEntryFileID;
|
|
SFSDirectory curdir = null;
|
|
foreach (EntryNameTableEntry e in FileNameTable.EntryNameTable)
|
|
{
|
|
for (int i = 0; i < nrdirs; i++)
|
|
{
|
|
if (offset == FileNameTable.DirectoryTable[i].dirEntryStart)
|
|
{
|
|
curdir = dirs[i];
|
|
break;
|
|
}
|
|
}
|
|
if (e is EntryNameTableEndOfDirectoryEntry)
|
|
{
|
|
curdir = null;
|
|
offset++;
|
|
}
|
|
else if (e is EntryNameTableFileEntry)
|
|
{
|
|
curdir.Files.Add(new SFSFile(fileid++, ((EntryNameTableFileEntry)e).entryName, curdir));
|
|
offset += 1u + e.entryNameLength;
|
|
}
|
|
else if (e is EntryNameTableDirectoryEntry)
|
|
{
|
|
if (treereconstruct)
|
|
{
|
|
dirs[((EntryNameTableDirectoryEntry)e).directoryID - 0xF000].Parent = curdir;
|
|
curdir.SubDirectories.Add(dirs[((EntryNameTableDirectoryEntry)e).directoryID - 0xF000]);
|
|
}
|
|
dirs[((EntryNameTableDirectoryEntry)e).directoryID - 0xF000].DirectoryName = ((EntryNameTableDirectoryEntry)e).entryName;
|
|
offset += 3u + (e.entryNameLength & 0x7Fu);
|
|
}
|
|
}
|
|
for (int i = 0; i < FileAllocationTable.Length; i++)
|
|
{
|
|
//byte[] data = new byte[FATB.allocationTable[i].fileSize];
|
|
//Array.Copy(FIMG.fileImage, FATB.allocationTable[i].fileTop, data, 0, data.Length);
|
|
dirs[0].GetFileByID((ushort)i).Data = FileData[i];// data;
|
|
}
|
|
return dirs[0];
|
|
}
|
|
|
|
public class UtilityBinIdentifier : FileFormatIdentifier
|
|
{
|
|
public override string GetCategory()
|
|
{
|
|
return Category_Archives;
|
|
}
|
|
|
|
public override string GetFileDescription()
|
|
{
|
|
return "NDS Wifi Archive (utility.bin)";
|
|
}
|
|
|
|
public override string GetFileFilter()
|
|
{
|
|
return "NDS Wifi Archive (utility.bin)|utility.bin";
|
|
}
|
|
|
|
public override Bitmap GetIcon()
|
|
{
|
|
return null;
|
|
}
|
|
|
|
public override FormatMatch IsFormat(EFEFile File)
|
|
{
|
|
if(File.Name.ToLower() == "utility.bin") return FormatMatch.Extension;
|
|
return FormatMatch.No;
|
|
}
|
|
|
|
}
|
|
}
|
|
}
|