Exemple #1
0
        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));
        }