private TreeNode InitializeNode(NDSFile file) { TreeNode node = new TreeNode(); if (file.NARCTables.IsValid) { node = new TreeNode(); node.Name = file.Name; node.Text = file.Name + file.Extension; node.Tag = file; node.ImageIndex = 3; node.SelectedImageIndex = 3; NARC narc = file.NARCTables; foreach (NDSDirectory child in narc.DirectoryTable[0].Children) { node.Nodes.Add(InitializeNode(child)); romTree.Update(); } foreach (NDSFile sub_file in narc.DirectoryTable[0].Contents) { node.Nodes.Add(InitializeNode(sub_file)); romTree.Update(); } } else { node = new FileNode(file); } return(node); }
private void romTree_AfterCollapse(object sender, TreeViewEventArgs e) { if (e.Node.Tag is NDSFile) { NDSFile file = e.Node.Tag as NDSFile; if (file.Extension == ".narc") { e.Node.ImageIndex = 3; e.Node.SelectedImageIndex = 3; } } if (e.Node.Tag is NDSDirectory || e.Node.Tag is "dir") { e.Node.ImageIndex = 0; e.Node.SelectedImageIndex = 0; } }
public NARC(Stream stream, NDSFile narc) { stream.Position = narc.Offset; byte[] bytes = new byte[narc.Length]; stream.Read(bytes, 0, narc.Length); try { if (BitConverter.ToUInt32(bytes, 0) != Signature || BitConverter.ToUInt16(bytes, 4) != ByteOrder || BitConverter.ToUInt16(bytes, 6) != Version || BitConverter.ToUInt32(bytes, 8) != bytes.Count() || BitConverter.ToUInt16(bytes, 12) != HeaderSize || BitConverter.ToUInt16(bytes, 14) != DataBlocks) { Console.WriteLine(narc.Path + narc.Name + narc.Extension + " has a malformed header."); } SizeFAT = BitConverter.ToUInt32(bytes, HeaderSize + 4); FilesFAT = BitConverter.ToUInt16(bytes, HeaderSize + 8); if (BitConverter.ToUInt32(bytes, 16) != SignatureFAT || SizeFAT != (FilesFAT * 8) + 12) { Console.WriteLine(narc.Path + narc.Name + narc.Extension + " has a malformed file allocation table data block."); } SizeFNT = BitConverter.ToUInt32(bytes, HeaderSize + Convert.ToInt32(SizeFAT) + 4); if (BitConverter.ToUInt32(bytes, HeaderSize + Convert.ToInt32(SizeFAT)) != SignatureFNT) { Console.WriteLine(narc.Path + narc.Name + narc.Extension + " has a malformed file name table data block."); } SizeIMG = BitConverter.ToUInt32(bytes, HeaderSize + Convert.ToInt32(SizeFAT + SizeFNT) + 4); if (BitConverter.ToInt32(bytes, HeaderSize + Convert.ToInt32(SizeFAT + SizeFNT)) != SignatureIMG) { Console.WriteLine(narc.Path + narc.Name + narc.Extension + " has a malformed file image data block."); } } catch { Console.WriteLine(narc.Path + narc.Name + narc.Extension + " is not a valid NARC file."); isValid = false; return; } byte[] fatArray = new byte[SizeFAT - 12]; byte[] fntArray = new byte[SizeFNT - 8]; Array.Copy(bytes, HeaderSize + 12, fatArray, 0, SizeFAT - 12); Array.Copy(bytes, HeaderSize + Convert.ToInt32(SizeFAT) + 8, fntArray, 0, SizeFNT - 8); int directoryCount = BitConverter.ToUInt16(fntArray, 6); string[] directories = new string[directoryCount]; directories[0] = narc.Path + "\\" + narc.Name + ".narc"; NDSFile[] files = new NDSFile[FilesFAT]; for (int i = 0; i < FilesFAT; i++) { NDSFile file = new NDSFile(); NDSFile currentFile = file; file.Name = "File " + i.ToString("D" + FilesFAT.ToString().Length); file.Path = directories[0]; file.Offset = Convert.ToInt32(BitConverter.ToUInt32(fatArray, i * 8)); file.Length = Convert.ToInt32(BitConverter.ToUInt32(fatArray, i * 8 + 4)) - file.Offset; file.Offset += narc.Offset + Convert.ToInt32(HeaderSize + SizeFAT + SizeFNT) + 8; files[i] = file; } int unnamedCount = 0; for (int i = 0; i < directoryCount; i++) { int entryPos = Convert.ToInt32(BitConverter.ToUInt32(fntArray, i * 8)); int fileIndex = Convert.ToInt32(BitConverter.ToUInt16(fntArray, (i * 8) + 4)); while (true) { byte entryByte = fntArray[entryPos++]; if (entryByte == 0) { break; } else if (entryByte == 128) { int index = BitConverter.ToUInt16(fntArray, entryPos) - 61440; directories[index] = directories[i] + "\\Unnamed " + unnamedCount++; entryPos += 2; } else if (entryByte > 128) { int index = BitConverter.ToUInt16(fntArray, (entryPos) + (entryByte - 128)) - 61440; directories[index] = directories[i] + "\\" + System.Text.Encoding.UTF8.GetString(fntArray, entryPos, entryByte - 128); entryPos += (entryByte - 128) + 2; } else { files[fileIndex].Name = System.Text.Encoding.UTF8.GetString(fntArray, entryPos, entryByte); files[fileIndex].Path = narc.Path + directories[i]; fileIndex++; entryPos += entryByte; } } } dirList = directories.ToList(); fileList = files.ToList(); foreach (NDSFile file in fileList) { file.GetExtension(stream); } bytes = null; fatArray = null; fntArray = null; directories = null; files = null; }
private void romTree_AfterSelect(object sender, TreeViewEventArgs e) { if (e.Node.Tag is NDSFile) { if (e.Node.Tag is NDSBinary) { DataGridView grid = propertyView; NDSBinary file = e.Node.Tag as NDSBinary; grid.Rows.Clear(); string[] row = new string[] { "Name:", file.Name + file.Extension }; grid.Rows.Add(row); row = new string[] { "Path:", "Root" }; grid.Rows.Add(row); if (file.Name.Contains("9")) { if (!file.Name.Contains("i")) { row = new string[] { "File Type:", "ARM9i Binary", "ARMv5TE Compiled Binary, DSi Extended" }; } else { row = new string[] { "File Type:", "ARM7i Binary", "ARMv5TE Compiled Binary." }; } } else if (file.Name.Contains("7")) { if (!file.Name.Contains("i")) { row = new string[] { "File Type:", "ARM9 Binary", "ARMv4T Compiled Binary, DSi Extended" }; } else { row = new string[] { "File Type:", "ARM7 Binary", "ARMv4T Compiled Binary." }; } } else { row = new string[] { "File Type:", "ARM Binary", "ARM Compiled Binary" }; } grid.Rows.Add(row); row = new string[] { "Address:", file.Offset.ToString(), "Offset from beginning of ROM to start of the binary." }; grid.Rows.Add(row); row = new string[] { "Length:", file.Length.ToString(), "The length of the binary." }; grid.Rows.Add(row); row = new string[] { "RAM Address:", file.Load.ToString(), "The RAM address this binary is loaded to." }; if (!file.Name.Contains("i")) { grid.Rows.Add(row); row = new string[] { "Hook Address:", file.AutoLoad.ToString(), "The RAM auto-load hook address." }; grid.Rows.Add(row); row = new string[] { "Parameters:", file.AutoParams.ToString(), "Offset to auto-load parameter table." }; } grid.Rows.Add(row); grid.Refresh(); } else if (e.Node.Tag is NDSOverlay) { DataGridView grid = propertyView; NDSOverlay file = e.Node.Tag as NDSOverlay; grid.Rows.Clear(); string[] row = new string[] { "Name:", file.Name + file.Extension }; grid.Rows.Add(row); row = new string[] { "Path:", file.Path }; grid.Rows.Add(row); if (file.Path.Contains("9")) { row = new string[] { "File Type:", "ARM9 Binary Overlay", "ARMv5TE Compiled Binary Overlay" }; } else if (file.Path.Contains("7")) { row = new string[] { "File Type:", "ARM7 Binary Overlay", "ARMv4T Compiled Binary Overlay" }; } else { row = new string[] { "File Type:", "ARM Binary Overlay", "ARM Compiled Binary Overlay" }; } grid.Rows.Add(row); row = new string[] { "File ID:", file.ID.ToString(), "The ID of the overlay in the file allocation table." }; grid.Rows.Add(row); row = new string[] { "Overlay ID:", file.OverlayID.ToString(), "The ID of the overlay in the overlay table." }; grid.Rows.Add(row); row = new string[] { "Offset:", "0x" + file.Offset.ToString("X"), "Offset from beginning of ROM to start of the overlay." }; grid.Rows.Add(row); row = new string[] { "Size:", file.Length.ToString("N0") + " bytes", "The length of the overlay." }; grid.Rows.Add(row); row = new string[] { "RAM Size:", file.SizeRAM.ToString("N0") + " bytes", "The size of the RAM section of the overlay." }; grid.Rows.Add(row); row = new string[] { "BSS Size:", file.SizeBSS.ToString("N0") + " bytes", "The size of the BSS section of the overlay." }; grid.Rows.Add(row); row = new string[] { "Compressed:", file.Compressed.ToString(), "Flag that indicates a compressed overlay." }; grid.Rows.Add(row); if (file.Compressed) { row = new string[] { "Compressed Size:", file.CompressedSize.ToString("N0") + " bytes", "The compressed size of the overlay." }; grid.Rows.Add(row); row = new string[] { "Total Size:", (file.SizeRAM + file.SizeBSS).ToString("N0") + " bytes", "The total size of the uncompressed overlay." }; grid.Rows.Add(row); } row = new string[] { "RAM Address:", "0x" + file.AddressRAM.ToString("X"), "The overlay transfer desitation address in RAM." }; grid.Rows.Add(row); row = new string[] { "Start Address:", "0x" + file.StaticStartAddress.ToString("X"), "The static initializer start address of the overlay." }; grid.Rows.Add(row); row = new string[] { "End Address:", "0x" + file.StaticEndAddress.ToString("X"), "The static initializer end address of the overlay." }; grid.Rows.Add(row); grid.Refresh(); } else if (e.Node.Tag is NDSBanner) { DataGridView grid = propertyView; NDSBanner banner = e.Node.Tag as NDSBanner; grid.Rows.Clear(); string[] row = new string[] { }; grid.Rows.Add(row); } else { DataGridView grid = propertyView; NDSFile file = e.Node.Tag as NDSFile; grid.Rows.Clear(); string[] row = new string[] { "Name:", file.Name + file.Extension }; grid.Rows.Add(row); row = new string[] { "Path:", file.Path }; grid.Rows.Add(row); row = new string[] { "File Type:", file.Type, file.About }; grid.Rows.Add(row); row = new string[] { "Offset:", file.Offset.ToString(), "The length of the file." }; grid.Rows.Add(row); row = new string[] { "Size:", file.Length.ToString(), "Offset from beginning of ROM to start of the overlay." }; grid.Rows.Add(row); grid.Refresh(); } } }
public FileNode(NDSFile node_file) { NodeFile = node_file; Name = NodeFile.Name; Text = NodeFile.Name + NodeFile.Extension; Tag = NodeFile; ImageIndex = 9; SelectedImageIndex = 9; switch (NodeFile.Extension) { case ".l": ImageIndex = 8; SelectedImageIndex = 8; break; case ".spa": ImageIndex = 14; SelectedImageIndex = 14; break; case ".bmg": ImageIndex = 15; SelectedImageIndex = 15; break; case ".txt": ImageIndex = 7; SelectedImageIndex = 7; break; case ".dat": ImageIndex = 9; SelectedImageIndex = 9; break; case ".bin": ImageIndex = 10; SelectedImageIndex = 10; break; case ".sdat": ImageIndex = 13; SelectedImageIndex = 13; break; case ".narc": ImageIndex = 3; SelectedImageIndex = 3; break; case ".arc": ImageIndex = 3; SelectedImageIndex = 3; break; case ".nce": ImageIndex = 11; SelectedImageIndex = 11; break; case ".nan": ImageIndex = 11; SelectedImageIndex = 11; break; case ".nam": ImageIndex = 11; SelectedImageIndex = 11; break; case ".ncg": ImageIndex = 11; SelectedImageIndex = 11; break; case ".ncb": ImageIndex = 11; SelectedImageIndex = 11; break; case ".ncl": ImageIndex = 11; SelectedImageIndex = 11; break; case ".nmc": ImageIndex = 11; SelectedImageIndex = 11; break; case ".nen": ImageIndex = 11; SelectedImageIndex = 11; break; case ".nsc": ImageIndex = 11; SelectedImageIndex = 11; break; case ".ncer": ImageIndex = 11; SelectedImageIndex = 11; break; case ".nanr": ImageIndex = 11; SelectedImageIndex = 11; break; case ".namr": ImageIndex = 11; SelectedImageIndex = 11; break; case ".ncgr": ImageIndex = 11; SelectedImageIndex = 11; break; case ".ncbr": ImageIndex = 11; SelectedImageIndex = 11; break; case ".nclr": ImageIndex = 11; SelectedImageIndex = 11; break; case ".nmcr": ImageIndex = 11; SelectedImageIndex = 11; break; case ".nenr": ImageIndex = 11; SelectedImageIndex = 11; break; case ".nscr": ImageIndex = 11; SelectedImageIndex = 11; break; case ".nftr": ImageIndex = 11; SelectedImageIndex = 11; break; case ".nsbmd": ImageIndex = 12; SelectedImageIndex = 12; break; case ".nsbtx": ImageIndex = 12; SelectedImageIndex = 12; break; case ".nsbca": ImageIndex = 12; SelectedImageIndex = 12; break; case ".nsbtp": ImageIndex = 12; SelectedImageIndex = 12; break; case ".nsbma": ImageIndex = 12; SelectedImageIndex = 12; break; case ".nsbva": ImageIndex = 12; SelectedImageIndex = 12; break; case ".nsbta": ImageIndex = 12; SelectedImageIndex = 12; break; } }
/// <summary> /// Reads and breaks down the file tables from the specified stream.<para /> /// Currently Supports: File Name Table (FNT); File Allocation Table (FAT); Overlay Tables (OVT); NARC. /// </summary> /// <param name="stream">ROM file to read tables from.</param> /// public void ReadFileTables(Stream stream) { if (FATOffset == 0 || FATLength == 0 || FNTOffset == 0 || FNTLength == 0 || ARM9OverlayOffset == 0 || ARM9OverlayLength == 0) { return; } // We initiate a BinaryReader with using() and set it to leave the stream open after disposal. using (BinaryReader reader = new BinaryReader(stream, new UTF8Encoding(), true)) { // The File Allocation Table contain eight bytes per file entry; // a four byte file offset followed by a four byte file length. // We start by determining the file count and then dividing the // offsets and lenghts into separate indexed arrays. int file_count = Convert.ToInt32(FATLength) / 8; FileTable = new NDSFile[file_count]; reader.BaseStream.Position = FATOffset; for (int i = 0; i < file_count; i++) { FileTable[i] = new NDSFile(); FileTable[i].ID = i; FileTable[i].Offset = reader.ReadUInt32(); FileTable[i].Length = reader.ReadUInt32() - FileTable[i].Offset; } // The File Name Table contains two sections; the first is the // main directory table. It's length is eight bytes per entry. // The first four bytes represent an offset to the entry in the // second section; the sub-directory table. This is followed by // two byte index corresponding to the first file entry in the // directory. Finally we have a two byte index corresponding to // the parent directory. // The first entry in the table is slightly different though. // The last two bytes in the first entry actually denote the // number of directories in the first section. Let's read that, // then use it to iterate through the main directory table and // split the table into three seperate indexed arrays. reader.BaseStream.Position = FNTOffset + 6; int directory_count = reader.ReadUInt16(); DirectoryTable = new NDSDirectory[directory_count]; int[] entry_offset = new int[directory_count]; int[] first_file = new int[directory_count]; int[] parent_index = new int[directory_count]; reader.BaseStream.Position = FNTOffset; for (int i = 0; i < directory_count; i++) { entry_offset[i] = Convert.ToInt32(reader.ReadUInt32() + FNTOffset); first_file[i] = reader.ReadUInt16(); parent_index[i] = reader.ReadUInt16() - 61440; } // Setting up the root directory. DirectoryTable[0] = new NDSDirectory(); DirectoryTable[0].Name = "File Table"; DirectoryTable[0].Path = "File Table"; // The second section is the sub-directory table. This table is // a bit more complex. We start by iterating through the main // directory table and using the entry offset to locate its // position in the sub-directory table. int file_index = first_file[0]; for (int i = 0; i < directory_count; i++) { // Initialize the directory arrays. reader.BaseStream.Position = entry_offset[i]; NDSDirectory parent_directory = DirectoryTable[i]; parent_directory.Children = new List <NDSDirectory>(); parent_directory.Contents = new List <NDSFile>(); while (true) { // A small sanity check to make sure we havent overrun the table. if (reader.BaseStream.Position > FNTOffset + FNTLength) { break; } // The first byte in the sub-directory entry indicates how to continue. byte entry_byte = reader.ReadByte(); // 0 indicates the end of a directory. if (entry_byte == 0) { break; } // 128 is actually invalid, and shouldn't be encountered. It would // indicate a directory with no name, which isn't actually valid. else if (entry_byte == 128) { continue; } // The first bit indicates a directory entry, so anything over 128 // is a sub-directory. The other seven bits indicate the length of // the sub-directory name. We simply need to subtract 128, and the // next so many bytes are the sub-directory name. The following // two bytes are the sub-directory's ID in the main directory table. else if (entry_byte > 128) { string name = System.Text.Encoding.UTF8.GetString(reader.ReadBytes(entry_byte - 128)); NDSDirectory child_directory = new NDSDirectory(); child_directory.Name = name; child_directory.Parent = parent_directory; child_directory.Path = parent_directory.Path + "\\" + child_directory.Name; child_directory.ID = reader.ReadUInt16() - 61440; DirectoryTable[child_directory.ID] = child_directory; parent_directory.Children.Add(child_directory); } // Anything under 128 indicates a file. Same as the previous, the // other seven bits indicate the length of the file name. Unlike // sub-directories, there is no index proceeding the file name. else { string name = System.Text.Encoding.UTF8.GetString(reader.ReadBytes(entry_byte)); NDSFile file = FileTable[file_index++]; file.Name = name; file.Parent = parent_directory; file.Path = parent_directory.Path + "\\" + file.Name; parent_directory.Contents.Add(file); } } } // We run this now so that we don't mess up the memory stream. foreach (NDSFile file in FileTable) { file.GetExtension(stream); if (file.Extension == ".narc") { file.NARCTables = FileHandler.NARC(stream, file, true); } else if (file.Extension == ".arc") { file.NARCTables = FileHandler.NARC(stream, file, false); } } // Header Represented As A File Header = new NDSFile(); Header.Name = "Header"; Header.Offset = 0; Header.Length = 16384; // Header Represented As A File Banner = new NDSBanner(); Banner.Name = "Banner"; Banner.Offset = BannerOffset; Banner.Length = 9216; Banner.ReadBanner(stream); // ARM9 and ARM9 Overlay Table reading. ARM9 = new NDSBinary(); ARM9.Name = "ARM9"; ARM9.Extension = ".bin"; ARM9.Offset = ARM9Offset; ARM9.Length = ARM9Length; ARM9.Load = ARM9Load; ARM9.AutoLoad = ARM9AutoLoad; ARM9.AutoParams = ARM9AutoParam; if (ARM9iLength > 0) { ARM9i = new NDSBinary(); ARM9i.Name = "ARM9i"; ARM9i.Extension = ".bin"; ARM9i.Offset = ARM9iOffset; ARM9i.Length = ARM9iLength; } int overlay9_count = Convert.ToInt32(ARM9OverlayLength / 32); reader.BaseStream.Position = ARM9OverlayOffset; OverlayTable9 = new NDSOverlay[overlay9_count]; for (int i = 0; i < overlay9_count; i++) { NDSOverlay overlay = new NDSOverlay(); overlay.OverlayID = reader.ReadUInt32(); overlay.AddressRAM = reader.ReadUInt32(); overlay.SizeRAM = reader.ReadUInt32(); overlay.SizeBSS = reader.ReadUInt32(); overlay.StaticStartAddress = reader.ReadUInt32(); overlay.StaticEndAddress = reader.ReadUInt32(); overlay.ID = Convert.ToInt32(reader.ReadUInt32()); overlay.Offset = FileTable[overlay.ID].Offset; overlay.Length = FileTable[overlay.ID].Length; uint temp = reader.ReadUInt32(); uint flag = temp >> 24; if ((flag & 2) != 0) { overlay.Authenticated = true; } if ((flag & 1) != 0) { overlay.Compressed = true; overlay.CompressedSize = (temp << 8) >> 8; } overlay.Name = "Overlay " + overlay.OverlayID; overlay.Path = "ARM9 Overlay Table\\" + overlay.Name; overlay.Extension = ".bin"; OverlayTable9[i] = overlay; FileTable[overlay.ID] = overlay; } // ARM7 and ARM7 Overlay Table reading. if (ARM7Length > 0) { ARM7 = new NDSBinary(); ARM7.Name = "ARM7"; ARM7.Extension = ".bin"; ARM7.Offset = ARM7Offset; ARM7.Length = ARM7Length; ARM7.Load = ARM7Load; ARM7.AutoLoad = ARM7AutoLoad; ARM7.AutoParams = ARM7AutoParam; } if (ARM7iLength > 0) { ARM7i = new NDSBinary(); ARM7i.Name = "ARM7i"; ARM7i.Extension = ".bin"; ARM7i.Offset = ARM7iOffset; ARM7i.Length = ARM7Length; } int overlay7_count = Convert.ToInt32(ARM7OverlayLength / 32); OverlayTable7 = new NDSOverlay[overlay7_count]; if (overlay7_count > 0) { reader.BaseStream.Position = ARM7OverlayOffset; for (int i = 0; i < overlay7_count; i++) { NDSOverlay overlay = new NDSOverlay(); overlay.OverlayID = reader.ReadUInt32(); overlay.AddressRAM = reader.ReadUInt32(); overlay.SizeRAM = reader.ReadUInt32(); overlay.SizeBSS = reader.ReadUInt32(); overlay.StaticStartAddress = reader.ReadUInt32(); overlay.StaticEndAddress = reader.ReadUInt32(); overlay.ID = Convert.ToInt32(reader.ReadUInt32()); overlay.Offset = FileTable[overlay.ID].Offset; overlay.Length = FileTable[overlay.ID].Length; uint temp = reader.ReadUInt32(); uint flag = temp >> 24; if ((flag & 2) != 0) { overlay.Authenticated = true; } if ((flag & 1) != 0) { overlay.Compressed = true; overlay.CompressedSize = (temp << 8) >> 8; } overlay.Name = "Overlay " + overlay.OverlayID; overlay.Path = "ARM9 Overlay Table\\" + overlay.Name; overlay.Extension = ".bin"; OverlayTable7[i] = overlay; FileTable[overlay.ID] = overlay; } } } }
public static NARC NARC(Stream stream, NDSFile narc, bool is_nitro) { NDSFile[] file_table; NDSDirectory[] directory_table; using (BinaryReader reader = new BinaryReader(stream, new UTF8Encoding(), true)) { reader.BaseStream.Position = narc.Offset; uint fnt_offset = 0; uint fnt_length = 0; uint fat_offset = 0; uint fat_length = 0; ushort file_count = 0; if (is_nitro) { // Here we read the header of the file. These are constants, // so if any of this is wrong, it's probably a malformed NARC // or not a NARC at all. if (reader.ReadUInt32() != 1129464142 || reader.ReadUInt16() != 65534 || reader.ReadUInt16() != 256 || reader.ReadUInt32() != narc.Length || reader.ReadUInt16() != 16 || reader.ReadUInt16() != 3) { return(new NARC(new NDSFile[0], new NDSDirectory[0], false)); } // FAT Signature Check if (reader.ReadUInt32() != 1178686530) { return(new NARC(new NDSFile[0], new NDSDirectory[0], false)); } fat_length = reader.ReadUInt32(); file_count = reader.ReadUInt16(); reader.BaseStream.Position += 2; // Another validity check ... if (fat_length != (file_count * 8) + 12) { return(new NARC(new NDSFile[0], new NDSDirectory[0], false)); } } else { fnt_offset = narc.Offset + reader.ReadUInt32(); fnt_length = reader.ReadUInt32(); fat_offset = narc.Offset + reader.ReadUInt32(); fat_length = reader.ReadUInt32(); file_count = Convert.ToUInt16(fat_length / 8); reader.BaseStream.Position = fat_offset; } // The File Allocation Table contain eight bytes per file entry; // a four byte file offset followed by a four byte file length. // We start by determining the file count and then dividing the // offsets and lenghts into separate indexed arrays. file_table = new NDSFile[file_count]; for (int i = 0; i < file_count; i++) { file_table[i] = new NDSFile(); file_table[i].Name = "File " + i.ToString("D" + file_count.ToString().Length); file_table[i].ID = i; file_table[i].Offset = reader.ReadUInt32(); file_table[i].Length = reader.ReadUInt32() - file_table[i].Offset; } if (is_nitro) { // FNT Signature check if (reader.ReadUInt32() != 1179538498) { return(new NARC(new NDSFile[0], new NDSDirectory[0], false)); } fnt_length = reader.ReadUInt32(); fnt_offset = narc.Offset + 16 + fat_length + 8; } // The File Name Table contains two sections; the first is the // main directory table. It's length is eight bytes per entry. // The first four bytes represent an offset to the entry in the // second section; the sub-directory table. This is followed by // two byte index corresponding to the first file entry in the // directory. Finally we have a two byte index corresponding to // the parent directory. // The first entry in the table is slightly different though. // The last two bytes in the first entry actually denote the // number of directories in the first section. Let's read that, // then use it to iterate through the main directory table and // split the table into three seperate indexed arrays. reader.BaseStream.Position = fnt_offset + 6; int directory_count = reader.ReadUInt16(); reader.BaseStream.Position = fnt_offset; // Setting up the root directory. directory_table = new NDSDirectory[directory_count]; directory_table[0] = new NDSDirectory(); directory_table[0].Name = narc.Name; directory_table[0].Path = narc.Path; int[] entry_offset = new int[directory_count]; int[] first_file = new int[directory_count]; int[] parent_index = new int[directory_count]; reader.BaseStream.Position = fnt_offset; for (int i = 0; i < directory_count; i++) { entry_offset[i] = Convert.ToInt32(reader.ReadUInt32() + fnt_offset); first_file[i] = reader.ReadUInt16(); parent_index[i] = reader.ReadUInt16() - 61440; } // The second section is the sub-directory table. This table is // a bit more complex. We start by iterating through the main // directory table and using the entry offset to locate its // position in the sub-directory table. int file_index = first_file[0]; for (int i = 0; i < directory_count; i++) { // Initialize the directory arrays. reader.BaseStream.Position = entry_offset[i]; NDSDirectory parent_directory = directory_table[i]; parent_directory.Children = new List <NDSDirectory>(); parent_directory.Contents = new List <NDSFile>(); while (true) { // A small sanity check to make sure we havent overrun the table. if (reader.BaseStream.Position > fnt_offset + fnt_length) { break; } // The first byte in the sub-directory entry indicates how to continue. byte entry_byte = reader.ReadByte(); // 0 indicates the end of a directory. if (entry_byte == 0) { break; } // 128 is actually invalid, and shouldn't be encountered. It would // indicate a directory with no name, which isn't actually valid. else if (entry_byte == 128) { continue; } // The first bit indicates a directory entry, so anything over 128 // is a sub-directory. The other seven bits indicate the length of // the sub-directory name. We simply need to subtract 128, and the // next so many bytes are the sub-directory name. The following // two bytes are the sub-directory's ID in the main directory table. else if (entry_byte > 128) { string name = System.Text.Encoding.UTF8.GetString(reader.ReadBytes(entry_byte - 128)); NDSDirectory child_directory = new NDSDirectory(); child_directory.Name = name; child_directory.Parent = parent_directory; child_directory.Path = parent_directory.Path + "\\" + child_directory.Name; child_directory.ID = reader.ReadUInt16() - 61440; directory_table[child_directory.ID] = child_directory; parent_directory.Children.Add(child_directory); } // Anything under 128 indicates a file. Same as the previous, the // other seven bits indicate the length of the file name. Unlike // sub-directories, there is no index proceeding the file name. else { string name = System.Text.Encoding.UTF8.GetString(reader.ReadBytes(entry_byte)); NDSFile file = file_table[file_index++]; file.Name = name; file.Parent = parent_directory; file.Path = parent_directory.Path + "\\" + file.Name; parent_directory.Contents.Add(file); } } } // We run this now so that we don't have to jump the memory stream a ton. foreach (NDSFile file in file_table) { if (file.Parent == null) { file.Parent = directory_table[0]; file.Path = file.Parent.Path + "\\" + file.Name; file.Parent.Contents.Add(file); } file.Offset += narc.Offset + 16 + fat_length + fnt_length + 8; file.GetExtension(stream); if (file.Extension == ".narc") { file.NARCTables = FileHandler.NARC(stream, file, true); } else if (file.Extension == ".arc") { file.NARCTables = FileHandler.NARC(stream, file, false); } } } return(new NARC(file_table, directory_table, true)); }